From 4e508da5326cda08113251c0271cdb66bd53d50a Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Wed, 9 Aug 2017 11:40:20 +1000 Subject: [PATCH 1/8] Lift add/edit link state to from FormatToolbar to Editable --- blocks/editable/format-toolbar/index.js | 78 +++++++------------------ blocks/editable/index.js | 37 ++++++++++-- 2 files changed, 52 insertions(+), 63 deletions(-) diff --git a/blocks/editable/format-toolbar/index.js b/blocks/editable/format-toolbar/index.js index f083ba449c1c5f..964b909cd253fe 100644 --- a/blocks/editable/format-toolbar/index.js +++ b/blocks/editable/format-toolbar/index.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { isUndefined } from 'lodash'; - /** * WordPress dependencies */ @@ -42,55 +37,33 @@ const DEFAULT_CONTROLS = [ 'bold', 'italic', 'strikethrough', 'link' ]; class FormatToolbar extends Component { constructor( props ) { - super( ...arguments ); - this.state = { - linkValue: props.formats.link ? props.formats.link.value : '', - isEditingLink: false, - }; + super( props ); + this.addLink = this.addLink.bind( this ); this.editLink = this.editLink.bind( this ); this.dropLink = this.dropLink.bind( this ); this.submitLink = this.submitLink.bind( this ); - this.updateLinkValue = this.updateLinkValue.bind( this ); this.onKeyDown = this.onKeyDown.bind( this ); + this.onLinkValueChange = this.onLinkValueChange.bind( this ); } componentDidMount() { document.addEventListener( 'keydown', this.onKeyDown ); } - componentWillUnmout() { - if ( this.editTimeout ) { - clearTimeout( this.editTimeout ); - } + componentWillUnmount() { document.removeEventListener( 'keydown', this.onKeyDown ); } onKeyDown( event ) { if ( event.keyCode === ESCAPE ) { - if ( this.state.isEditingLink ) { + if ( this.props.isEditingLink ) { event.stopPropagation(); this.dropLink(); } } } - componentWillReceiveProps( nextProps ) { - // Update the link value if the focused link node changes - if ( - isUndefined( nextProps.formats.link ) !== isUndefined( this.props.formats.link ) || - ( - nextProps.formats.link && this.props.formats.link && - nextProps.formats.link.node !== this.props.formats.link.node - ) - ) { - this.setState( { - linkValue: nextProps.formats.link ? nextProps.formats.link.value : '', - isEditingLink: false, - } ); - } - } - toggleFormat( format ) { return () => { this.props.onChange( { @@ -100,12 +73,7 @@ class FormatToolbar extends Component { } addLink() { - if ( ! this.props.formats.link ) { - this.props.onChange( { link: { value: '' } } ); - - // Debounce the call to avoid the reset in willReceiveProps - this.editTimeout = setTimeout( () => this.setState( { isEditingLink: true } ) ); - } + this.props.onAddLink(); } dropLink() { @@ -114,31 +82,25 @@ class FormatToolbar extends Component { editLink( event ) { event.preventDefault(); - this.setState( { - isEditingLink: true, - } ); + this.props.onEditLink(); + + this.setState( { linkValue: this.props.formats.link.value } ); } submitLink( event ) { event.preventDefault(); - this.props.onChange( { link: { value: this.state.linkValue } } ); - this.setState( { - isEditingLink: false, - } ); - if ( - this.props.formats.link.value === '' && - !! this.state.linkValue.length - ) { + this.props.onChange( { link: { value: this.props.newLinkValue } } ); + if ( this.props.isAddingLink ) { this.props.speak( __( 'Link inserted.' ), 'assertive' ); } } - updateLinkValue( linkValue ) { - this.setState( { linkValue } ); + onLinkValueChange( event ) { + this.setState( { linkValue: event } ); } render() { - const { formats, focusPosition, enabledControls = DEFAULT_CONTROLS } = this.props; + const { formats, focusPosition, isAddingLink, isEditingLink, newLinkValue, enabledControls = DEFAULT_CONTROLS } = this.props; const linkStyle = focusPosition ? { position: 'absolute', ...focusPosition } : null; @@ -156,7 +118,7 @@ class FormatToolbar extends Component { icon: 'admin-links', title: __( 'Link' ), onClick: this.addLink, - isActive: !! formats.link, + isActive: isAddingLink || !! formats.link, } ); } @@ -164,25 +126,25 @@ class FormatToolbar extends Component {
- { !! formats.link && this.state.isEditingLink && + { ( isAddingLink || isEditingLink ) &&
- + } - { !! formats.link && ! this.state.isEditingLink && + { !! formats.link && ! isAddingLink && ! isEditingLink &&
- { this.state.linkValue && decodeURI( this.state.linkValue ) } + { formats.link.value && decodeURI( formats.link.value ) } diff --git a/blocks/editable/index.js b/blocks/editable/index.js index 5ab6bb12e6a309..6c9c76d394bad5 100644 --- a/blocks/editable/index.js +++ b/blocks/editable/index.js @@ -68,11 +68,19 @@ export default class Editable extends Component { this.onSelectionChange = this.onSelectionChange.bind( this ); this.maybePropagateUndo = this.maybePropagateUndo.bind( this ); this.onPastePostProcess = this.onPastePostProcess.bind( this ); + this.onToolbarAddLink = this.onToolbarAddLink.bind( this ); + this.onToolbarEditLink = this.onToolbarEditLink.bind( this ); + this.onToolbarChangeLinkValue = this.onToolbarChangeLinkValue.bind( this ); this.state = { formats: {}, bookmark: null, empty: ! props.value || ! props.value.length, + toolbar: { + isAddingLink: false, + isEditingLink: false, + newLinkValue: '', + }, }; } @@ -202,8 +210,9 @@ export default class Editable extends Component { this.props.onChange( this.savedContent ); } - getRelativePosition( node ) { - const position = node.getBoundingClientRect(); + getFocusPosition() { + const range = this.editor.selection.getRng(); + const position = range.getBoundingClientRect(); // Find the parent "relative" positioned container const container = this.props.inlineToolbar @@ -403,7 +412,19 @@ export default class Editable extends Component { ); } - onNodeChange( { element, parents } ) { + onToolbarAddLink() { + this.setState( { toolbar: { isEditingLink: false, isAddingLink: true, newLinkValue: '' } } ); + } + + onToolbarEditLink() { + this.setState( { toolbar: { isEditingLink: false, isAddingLink: true, newLinkValue: this.state.formats.link.value } } ); + } + + onToolbarChangeLinkValue( value ) { + this.setState( { toolbar: { ...this.state.toolbar, newLinkValue: value } } ); + } + + onNodeChange( { parents } ) { const formats = {}; const link = find( parents, ( node ) => node.nodeName.toLowerCase() === 'a' ); if ( link ) { @@ -412,9 +433,9 @@ export default class Editable extends Component { const activeFormats = this.editor.formatter.matchAll( [ 'bold', 'italic', 'strikethrough' ] ); activeFormats.forEach( ( activeFormat ) => formats[ activeFormat ] = true ); - const focusPosition = this.getRelativePosition( element ); + const focusPosition = this.getFocusPosition(); const bookmark = this.editor.selection.getBookmark( 2, true ); - this.setState( { bookmark, formats, focusPosition } ); + this.setState( { bookmark, formats, focusPosition, toolbar: { isAddingLink: false, isEditingLink: false, newLinkValue: '' } } ); } updateContent() { @@ -501,6 +522,8 @@ export default class Editable extends Component { } else { this.editor.execCommand( 'Unlink' ); } + + this.setState( { toolbar: { isEditingLink: false, isAddingLink: false, newLinkValue: '' } } ); } else { const isActive = this.isFormatActive( format ); if ( isActive && ! formatValue ) { @@ -544,6 +567,10 @@ export default class Editable extends Component { formats={ this.state.formats } onChange={ this.changeFormats } enabledControls={ formattingControls } + onChangeLinkValue={ this.onToolbarChangeLinkValue } + onAddLink={ this.onToolbarAddLink } + onEditLink={ this.onToolbarEditLink } + { ...this.state.toolbar } /> ); From c15ef07d4e9b5ce1796ab51075516344a6d036b3 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Wed, 9 Aug 2017 11:59:30 +1000 Subject: [PATCH 2/8] Remove unused code --- blocks/editable/format-toolbar/index.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/blocks/editable/format-toolbar/index.js b/blocks/editable/format-toolbar/index.js index 964b909cd253fe..7cc26a74ff50b5 100644 --- a/blocks/editable/format-toolbar/index.js +++ b/blocks/editable/format-toolbar/index.js @@ -83,8 +83,6 @@ class FormatToolbar extends Component { editLink( event ) { event.preventDefault(); this.props.onEditLink(); - - this.setState( { linkValue: this.props.formats.link.value } ); } submitLink( event ) { @@ -95,10 +93,6 @@ class FormatToolbar extends Component { } } - onLinkValueChange( event ) { - this.setState( { linkValue: event } ); - } - render() { const { formats, focusPosition, isAddingLink, isEditingLink, newLinkValue, enabledControls = DEFAULT_CONTROLS } = this.props; const linkStyle = focusPosition From 1313137e9e2a9707da38d60b9e248139483f7a38 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Wed, 9 Aug 2017 14:00:42 +1000 Subject: [PATCH 3/8] Remove unused code --- blocks/editable/format-toolbar/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/blocks/editable/format-toolbar/index.js b/blocks/editable/format-toolbar/index.js index 7cc26a74ff50b5..1ef2d9fae58ee8 100644 --- a/blocks/editable/format-toolbar/index.js +++ b/blocks/editable/format-toolbar/index.js @@ -44,7 +44,6 @@ class FormatToolbar extends Component { this.dropLink = this.dropLink.bind( this ); this.submitLink = this.submitLink.bind( this ); this.onKeyDown = this.onKeyDown.bind( this ); - this.onLinkValueChange = this.onLinkValueChange.bind( this ); } componentDidMount() { From c1328afd853258a8350ddadb2ff5ae0ee4f21069 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Thu, 10 Aug 2017 15:35:44 +1000 Subject: [PATCH 4/8] Fix FireFox add link error --- blocks/editable/index.js | 26 +++++++++++++++----------- editor/modes/visual-editor/block.js | 2 +- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/blocks/editable/index.js b/blocks/editable/index.js index 6c9c76d394bad5..43c08a39560a53 100644 --- a/blocks/editable/index.js +++ b/blocks/editable/index.js @@ -74,7 +74,6 @@ export default class Editable extends Component { this.state = { formats: {}, - bookmark: null, empty: ! props.value || ! props.value.length, toolbar: { isAddingLink: false, @@ -88,6 +87,7 @@ export default class Editable extends Component { return ( this.props.getSettings || identity )( { ...settings, forced_root_block: this.props.multiline || false, + custom_ui_selector: '.tinymce-custom-ui *', } ); } @@ -434,8 +434,7 @@ export default class Editable extends Component { activeFormats.forEach( ( activeFormat ) => formats[ activeFormat ] = true ); const focusPosition = this.getFocusPosition(); - const bookmark = this.editor.selection.getBookmark( 2, true ); - this.setState( { bookmark, formats, focusPosition, toolbar: { isAddingLink: false, isEditingLink: false, newLinkValue: '' } } ); + this.setState( { formats, focusPosition, toolbar: { isAddingLink: false, isEditingLink: false, newLinkValue: '' } } ); } updateContent() { @@ -506,19 +505,24 @@ export default class Editable extends Component { return !! this.state.formats[ format ]; } + removeFormat( format ) { + this.editor.focus(); + this.editor.formatter.remove( format ); + } + applyFormat( format, args, node ) { + this.editor.focus(); + this.editor.formatter.apply( format, args, node ); + } + changeFormats( formats ) { forEach( formats, ( formatValue, format ) => { if ( format === 'link' ) { - if ( this.state.bookmark ) { - this.editor.selection.moveToBookmark( this.state.bookmark ); - } - if ( formatValue !== undefined ) { const anchor = this.editor.dom.getParent( this.editor.selection.getNode(), 'a' ); if ( ! anchor ) { - this.editor.formatter.remove( 'link' ); + this.removeFormat( 'link' ); } - this.editor.formatter.apply( 'link', { href: formatValue.value }, anchor ); + this.applyFormat( 'link', { href: formatValue.value }, anchor ); } else { this.editor.execCommand( 'Unlink' ); } @@ -527,9 +531,9 @@ export default class Editable extends Component { } else { const isActive = this.isFormatActive( format ); if ( isActive && ! formatValue ) { - this.editor.formatter.remove( format ); + this.removeFormat( format ); } else if ( ! isActive && formatValue ) { - this.editor.formatter.apply( format ); + this.applyFormat( format ); } } } ); diff --git a/editor/modes/visual-editor/block.js b/editor/modes/visual-editor/block.js index 818687ee9a74f4..e317200486990a 100644 --- a/editor/modes/visual-editor/block.js +++ b/editor/modes/visual-editor/block.js @@ -379,7 +379,7 @@ class VisualEditorBlock extends Component { transitionLeave={ false } component={ FirstChild } > -
+
From 61f89695d4b37104f5a51e76942fc1fdd8ca4465 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Fri, 11 Aug 2017 10:19:52 +1000 Subject: [PATCH 5/8] Pass arguments to super --- blocks/editable/format-toolbar/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blocks/editable/format-toolbar/index.js b/blocks/editable/format-toolbar/index.js index 1ef2d9fae58ee8..e25d3ad1a6d4e8 100644 --- a/blocks/editable/format-toolbar/index.js +++ b/blocks/editable/format-toolbar/index.js @@ -36,8 +36,8 @@ const FORMATTING_CONTROLS = [ const DEFAULT_CONTROLS = [ 'bold', 'italic', 'strikethrough', 'link' ]; class FormatToolbar extends Component { - constructor( props ) { - super( props ); + constructor() { + super( ...arguments ); this.addLink = this.addLink.bind( this ); this.editLink = this.editLink.bind( this ); From 6724402928148fea52af0315ffd5cf2fdbdf4617 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Mon, 14 Aug 2017 13:40:25 +1000 Subject: [PATCH 6/8] Moved FormatToolbar state from Editable back to FormatToolbar --- blocks/editable/format-toolbar/index.js | 35 ++++++++++++++++++++----- blocks/editable/index.js | 29 +++----------------- editor/modes/visual-editor/block.js | 2 +- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/blocks/editable/format-toolbar/index.js b/blocks/editable/format-toolbar/index.js index e25d3ad1a6d4e8..ebf57af3aa57d8 100644 --- a/blocks/editable/format-toolbar/index.js +++ b/blocks/editable/format-toolbar/index.js @@ -39,11 +39,28 @@ class FormatToolbar extends Component { constructor() { super( ...arguments ); + this.state = { + isAddingLink: false, + isEditingLink: false, + newLinkValue: '', + }; + this.addLink = this.addLink.bind( this ); this.editLink = this.editLink.bind( this ); this.dropLink = this.dropLink.bind( this ); this.submitLink = this.submitLink.bind( this ); this.onKeyDown = this.onKeyDown.bind( this ); + this.onChangeLinkValue = this.onChangeLinkValue.bind( this ); + } + + componentWillReceiveProps( nextProps ) { + if ( this.props.selectedNodeId !== nextProps.selectedNodeId ) { + this.setState( { + isAddingLink: false, + isEditingLink: false, + newLinkValue: '', + } ); + } } componentDidMount() { @@ -63,6 +80,10 @@ class FormatToolbar extends Component { } } + onChangeLinkValue( value ) { + this.setState( { newLinkValue: value } ); + } + toggleFormat( format ) { return () => { this.props.onChange( { @@ -72,28 +93,30 @@ class FormatToolbar extends Component { } addLink() { - this.props.onAddLink(); + this.setState( { isEditingLink: false, isAddingLink: true, newLinkValue: '' } ); } dropLink() { this.props.onChange( { link: undefined } ); + this.setState( { isEditingLink: false, isAddingLink: false, newLinkValue: '' } ); } editLink( event ) { event.preventDefault(); - this.props.onEditLink(); + this.setState( { isEditingLink: false, isAddingLink: true, newLinkValue: this.props.formats.link.value } ); } submitLink( event ) { event.preventDefault(); - this.props.onChange( { link: { value: this.props.newLinkValue } } ); - if ( this.props.isAddingLink ) { + this.props.onChange( { link: { value: this.state.newLinkValue } } ); + if ( this.state.isAddingLink ) { this.props.speak( __( 'Link inserted.' ), 'assertive' ); } } render() { - const { formats, focusPosition, isAddingLink, isEditingLink, newLinkValue, enabledControls = DEFAULT_CONTROLS } = this.props; + const { formats, focusPosition, enabledControls = DEFAULT_CONTROLS } = this.props; + const { isAddingLink, isEditingLink, newLinkValue } = this.state; const linkStyle = focusPosition ? { position: 'absolute', ...focusPosition } : null; @@ -124,7 +147,7 @@ class FormatToolbar extends Component { className="blocks-format-toolbar__link-modal" style={ linkStyle } onSubmit={ this.submitLink }> - + diff --git a/blocks/editable/index.js b/blocks/editable/index.js index 43c08a39560a53..71712c9f9b9823 100644 --- a/blocks/editable/index.js +++ b/blocks/editable/index.js @@ -68,18 +68,11 @@ export default class Editable extends Component { this.onSelectionChange = this.onSelectionChange.bind( this ); this.maybePropagateUndo = this.maybePropagateUndo.bind( this ); this.onPastePostProcess = this.onPastePostProcess.bind( this ); - this.onToolbarAddLink = this.onToolbarAddLink.bind( this ); - this.onToolbarEditLink = this.onToolbarEditLink.bind( this ); - this.onToolbarChangeLinkValue = this.onToolbarChangeLinkValue.bind( this ); this.state = { formats: {}, empty: ! props.value || ! props.value.length, - toolbar: { - isAddingLink: false, - isEditingLink: false, - newLinkValue: '', - }, + selectedNodeId: 0, }; } @@ -87,7 +80,6 @@ export default class Editable extends Component { return ( this.props.getSettings || identity )( { ...settings, forced_root_block: this.props.multiline || false, - custom_ui_selector: '.tinymce-custom-ui *', } ); } @@ -412,18 +404,6 @@ export default class Editable extends Component { ); } - onToolbarAddLink() { - this.setState( { toolbar: { isEditingLink: false, isAddingLink: true, newLinkValue: '' } } ); - } - - onToolbarEditLink() { - this.setState( { toolbar: { isEditingLink: false, isAddingLink: true, newLinkValue: this.state.formats.link.value } } ); - } - - onToolbarChangeLinkValue( value ) { - this.setState( { toolbar: { ...this.state.toolbar, newLinkValue: value } } ); - } - onNodeChange( { parents } ) { const formats = {}; const link = find( parents, ( node ) => node.nodeName.toLowerCase() === 'a' ); @@ -434,7 +414,7 @@ export default class Editable extends Component { activeFormats.forEach( ( activeFormat ) => formats[ activeFormat ] = true ); const focusPosition = this.getFocusPosition(); - this.setState( { formats, focusPosition, toolbar: { isAddingLink: false, isEditingLink: false, newLinkValue: '' } } ); + this.setState( { formats, focusPosition, selectedNodeId: this.state.selectedNodeId + 1 } ); } updateContent() { @@ -567,14 +547,11 @@ export default class Editable extends Component { const formatToolbar = ( ); diff --git a/editor/modes/visual-editor/block.js b/editor/modes/visual-editor/block.js index e317200486990a..818687ee9a74f4 100644 --- a/editor/modes/visual-editor/block.js +++ b/editor/modes/visual-editor/block.js @@ -379,7 +379,7 @@ class VisualEditorBlock extends Component { transitionLeave={ false } component={ FirstChild } > -
+
From 1f72dfbf15b7952ac42be283225b67b0f79fb3e5 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Mon, 14 Aug 2017 13:53:04 +1000 Subject: [PATCH 7/8] Remove unused code --- blocks/editable/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/blocks/editable/index.js b/blocks/editable/index.js index 71712c9f9b9823..81dba068f1ea7c 100644 --- a/blocks/editable/index.js +++ b/blocks/editable/index.js @@ -506,8 +506,6 @@ export default class Editable extends Component { } else { this.editor.execCommand( 'Unlink' ); } - - this.setState( { toolbar: { isEditingLink: false, isAddingLink: false, newLinkValue: '' } } ); } else { const isActive = this.isFormatActive( format ); if ( isActive && ! formatValue ) { From 620892ae6b6043d1669de4ceef5acaceb5497e9e Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Mon, 14 Aug 2017 14:13:17 +1000 Subject: [PATCH 8/8] Fix onKeyDown --- blocks/editable/format-toolbar/index.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/blocks/editable/format-toolbar/index.js b/blocks/editable/format-toolbar/index.js index ebf57af3aa57d8..3397ca7da36b3b 100644 --- a/blocks/editable/format-toolbar/index.js +++ b/blocks/editable/format-toolbar/index.js @@ -53,16 +53,6 @@ class FormatToolbar extends Component { this.onChangeLinkValue = this.onChangeLinkValue.bind( this ); } - componentWillReceiveProps( nextProps ) { - if ( this.props.selectedNodeId !== nextProps.selectedNodeId ) { - this.setState( { - isAddingLink: false, - isEditingLink: false, - newLinkValue: '', - } ); - } - } - componentDidMount() { document.addEventListener( 'keydown', this.onKeyDown ); } @@ -73,13 +63,23 @@ class FormatToolbar extends Component { onKeyDown( event ) { if ( event.keyCode === ESCAPE ) { - if ( this.props.isEditingLink ) { + if ( this.state.isEditingLink ) { event.stopPropagation(); this.dropLink(); } } } + componentWillReceiveProps( nextProps ) { + if ( this.props.selectedNodeId !== nextProps.selectedNodeId ) { + this.setState( { + isAddingLink: false, + isEditingLink: false, + newLinkValue: '', + } ); + } + } + onChangeLinkValue( value ) { this.setState( { newLinkValue: value } ); }