Skip to content

Commit

Permalink
Visual Editor: Keep track of the focused block (#438)
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad authored Apr 20, 2017
1 parent 3bba830 commit c970c16
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 37 deletions.
29 changes: 28 additions & 1 deletion blocks/components/editable/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default class Editable extends wp.element.Component {
this.onChange = this.onChange.bind( this );
this.onNewBlock = this.onNewBlock.bind( this );
this.bindNode = this.bindNode.bind( this );
this.onFocus = this.onFocus.bind( this );
}

componentDidMount() {
Expand Down Expand Up @@ -45,11 +46,22 @@ export default class Editable extends wp.element.Component {
editor.on( 'init', this.onInit );
editor.on( 'focusout', this.onChange );
editor.on( 'NewBlock', this.onNewBlock );
editor.on( 'focusin', this.onFocus );
}

onInit() {
const { value = '' } = this.props;
this.editor.setContent( value );
this.focus();
}

onFocus() {
if ( ! this.props.onFocus ) {
return;
}

// TODO: We need a way to save the focus position ( bookmark maybe )
this.props.onFocus();
}

onChange() {
Expand Down Expand Up @@ -107,6 +119,12 @@ export default class Editable extends wp.element.Component {
this.editor.selection.moveToBookmark( bookmark );
}

focus() {
if ( this.props.focus ) {
this.editor.focus();
}
}

componentWillUpdate( nextProps ) {
if ( this.editor && this.props.tagName !== nextProps.tagName ) {
this.editor.destroy();
Expand All @@ -122,7 +140,16 @@ export default class Editable extends wp.element.Component {
componentDidUpdate( prevProps ) {
if ( this.props.tagName !== prevProps.tagName ) {
this.initialize();
} else if ( this.props.value !== prevProps.value ) {
}

if ( !! this.props.focus && ! prevProps.focus ) {
this.focus();
}

if (
this.props.tagName === prevProps.tagName &&
this.props.value !== prevProps.value
) {
this.updateContent();
}
}
Expand Down
4 changes: 3 additions & 1 deletion blocks/library/heading/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@ registerBlock( 'core/heading', {
} ) )
],

edit( { attributes, setAttributes } ) {
edit( { attributes, setAttributes, focus, setFocus } ) {
const { content, tag, align } = attributes;

return (
<Editable
tagName={ tag }
value={ content }
focus={ focus }
onFocus={ setFocus }
onChange={ ( value ) => setAttributes( { content: value } ) }
style={ align ? { textAlign: align } : null }
/>
Expand Down
6 changes: 4 additions & 2 deletions blocks/library/image/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,19 @@ registerBlock( 'core/image', {
caption: html( 'figcaption' )
},

edit( { attributes, isSelected, setAttributes } ) {
edit( { attributes, setAttributes, focus, setFocus } ) {
const { url, alt, caption } = attributes;

return (
<figure>
<img src={ url } alt={ alt } />
{ caption || isSelected ? (
{ caption || !! focus ? (
<Editable
tagName="figcaption"
placeholder={ wp.i18n.__( 'Write caption…' ) }
value={ caption }
focus={ focus }
onFocus={ setFocus }
onChange={ ( value ) => setAttributes( { caption: value } ) } />
) : null }
</figure>
Expand Down
4 changes: 3 additions & 1 deletion blocks/library/list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ registerBlock( 'core/list', {
}
],

edit( { attributes } ) {
edit( { attributes, focus, setFocus } ) {
const { listType = 'ol', items = [], align } = attributes;
const content = items.map( item => {
return `<li>${ item.value }</li>`;
Expand All @@ -65,6 +65,8 @@ registerBlock( 'core/list', {
tagName={ listType }
style={ align ? { textAlign: align } : null }
value={ content }
focus={ focus }
onFocus={ setFocus }
className="blocks-list" />
);
},
Expand Down
30 changes: 19 additions & 11 deletions blocks/library/quote/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ registerBlock( 'core/quote', {
citation: html( 'footer' )
},

edit( { attributes, setAttributes } ) {
edit( { attributes, setAttributes, focus, setFocus } ) {
const { value, citation } = attributes;

return (
Expand All @@ -31,16 +31,24 @@ registerBlock( 'core/quote', {
( paragraphs ) => setAttributes( {
value: fromParagraphsToValue( paragraphs )
} )
} />
<footer>
<Editable
value={ citation }
onChange={
( newValue ) => setAttributes( {
citation: newValue
} )
} />
</footer>
}
focus={ focus && focus.editable === 'value' ? focus : null }
onFocus={ () => setFocus( { editable: 'value' } ) }
/>
{ ( citation || !! focus ) &&
<footer>
<Editable
value={ citation }
onChange={
( newValue ) => setAttributes( {
citation: newValue
} )
}
focus={ focus && focus.editable === 'citation' ? focus : null }
onFocus={ () => setFocus( { editable: 'citation' } ) }
/>
</footer>
}
</blockquote>
);
},
Expand Down
4 changes: 3 additions & 1 deletion blocks/library/text/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ registerBlock( 'core/text', {
}
],

edit( { attributes, setAttributes, insertBlockAfter } ) {
edit( { attributes, setAttributes, insertBlockAfter, focus, setFocus } ) {
const { content, align } = attributes;

return (
Expand All @@ -56,6 +56,8 @@ registerBlock( 'core/text', {
onChange={ ( paragraphs ) => setAttributes( {
content: fromParagraphsToValue( paragraphs )
} ) }
focus={ focus }
onFocus={ setFocus }
style={ align ? { textAlign: align } : null }
onSplit={ ( before, after ) => {
setAttributes( { content: fromParagraphsToValue( before ) } );
Expand Down
51 changes: 39 additions & 12 deletions editor/modes/visual-editor/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class VisualEditorBlock extends wp.element.Component {
this.bindBlockNode = this.bindBlockNode.bind( this );
this.setAttributes = this.setAttributes.bind( this );
this.maybeDeselect = this.maybeDeselect.bind( this );
this.maybeHover = this.maybeHover.bind( this );
this.previousOffset = null;
}

Expand Down Expand Up @@ -44,6 +45,13 @@ class VisualEditorBlock extends wp.element.Component {
} );
}

maybeHover() {
const { isTyping, isHovered, onHover } = this.props;
if ( isTyping && ! isHovered ) {
onHover();
}
}

maybeDeselect( event ) {
// Annoyingly React does not support focusOut and we're forced to check
// related target to ensure it's not a child when blur fires.
Expand Down Expand Up @@ -75,13 +83,13 @@ class VisualEditorBlock extends wp.element.Component {
return null;
}

const { isHovered, isSelected } = this.props;
const { isHovered, isSelected, isTyping, focus } = this.props;
const className = classnames( 'editor-visual-editor__block', {
'is-selected': isSelected,
'is-selected': isSelected && ! isTyping,
'is-hovered': isHovered
} );

const { onSelect, onDeselect, onMouseEnter, onMouseLeave, onInsertAfter } = this.props;
const { onSelect, onStartTyping, onHover, onMouseLeave, onFocus, onInsertAfter } = this.props;

// Disable reason: Each block can receive focus but must be able to contain
// block children. Tab keyboard navigation enabled by tabIndex assignment.
Expand All @@ -93,15 +101,16 @@ class VisualEditorBlock extends wp.element.Component {
tabIndex="0"
onFocus={ onSelect }
onBlur={ this.maybeDeselect }
onKeyDown={ onDeselect }
onMouseEnter={ onMouseEnter }
onKeyDown={ onStartTyping }
onMouseEnter={ onHover }
onMouseMove={ this.maybeHover }
onMouseLeave={ onMouseLeave }
className={ className }
>
{ ( isSelected || isHovered ) && <BlockMover uid={ block.uid } /> }
{ ( ( isSelected && ! isTyping ) || isHovered ) && <BlockMover uid={ block.uid } /> }
<div className="editor-visual-editor__block-controls">
{ isSelected && <BlockSwitcher uid={ block.uid } /> }
{ isSelected && settings.controls ? (
{ isSelected && ! isTyping && <BlockSwitcher uid={ block.uid } /> }
{ isSelected && ! isTyping && settings.controls ? (
<Toolbar
controls={ settings.controls.map( ( control ) => ( {
...control,
Expand All @@ -111,10 +120,11 @@ class VisualEditorBlock extends wp.element.Component {
) : null }
</div>
<BlockEdit
isSelected={ isSelected }
focus={ focus }
attributes={ block.attributes }
setAttributes={ this.setAttributes }
insertBlockAfter={ onInsertAfter }
setFocus={ onFocus }
/>
</div>
);
Expand All @@ -126,8 +136,10 @@ export default connect(
( state, ownProps ) => ( {
order: state.blocks.order.indexOf( ownProps.uid ),
block: state.blocks.byUid[ ownProps.uid ],
isSelected: state.selectedBlock === ownProps.uid,
isHovered: state.hoveredBlock === ownProps.uid
isSelected: state.selectedBlock.uid === ownProps.uid,
isHovered: state.hoveredBlock === ownProps.uid,
focus: state.selectedBlock.uid === ownProps.uid ? state.selectedBlock.focus : null,
isTyping: state.selectedBlock.uid === ownProps.uid ? state.selectedBlock.typing : false,
} ),
( dispatch, ownProps ) => ( {
onChange( updates ) {
Expand All @@ -151,7 +163,13 @@ export default connect(
uid: ownProps.uid
} );
},
onMouseEnter() {
onStartTyping() {
dispatch( {
type: 'START_TYPING',
uid: ownProps.uid
} );
},
onHover() {
dispatch( {
type: 'TOGGLE_BLOCK_HOVERED',
hovered: true,
Expand All @@ -165,12 +183,21 @@ export default connect(
uid: ownProps.uid
} );
},

onInsertAfter( block ) {
dispatch( {
type: 'INSERT_BLOCK',
after: ownProps.uid,
block
} );
},

onFocus( config ) {
dispatch( {
type: 'UPDATE_FOCUS',
uid: ownProps.uid,
config
} );
}
} )
)( VisualEditorBlock );
42 changes: 38 additions & 4 deletions editor/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,49 @@ export const blocks = combineUndoableReducers( {
* @param {Object} action Dispatched action
* @return {Object} Updated state
*/
export function selectedBlock( state = null, action ) {
export function selectedBlock( state = {}, action ) {
switch ( action.type ) {
case 'TOGGLE_BLOCK_SELECTED':
return action.selected ? action.uid : null;
if ( ! action.selected ) {
return state.uid === action.uid ? {} : state;
}
return action.uid === state.uid
? state
: { uid: action.uid, typing: false, focus: {} };

case 'MOVE_BLOCK_UP':
case 'MOVE_BLOCK_DOWN':
return action.uid;
return action.uid === state.uid
? state
: { uid: action.uid, typing: false, focus: {} };

case 'INSERT_BLOCK':
return action.block.uid;
return {
uid: action.block.uid,
typing: false,
focus: {}
};

case 'UPDATE_FOCUS':
return {
uid: action.uid,
typing: state.uid === action.uid ? state.typing : false,
focus: action.config || {}
};

case 'START_TYPING':
if ( action.uid !== state.uid ) {
return {
uid: action.uid,
typing: true,
focus: {}
};
}

return {
...state,
typing: true
};
}

return state;
Expand All @@ -133,6 +165,8 @@ export function hoveredBlock( state = null, action ) {
return null;
}
break;
case 'START_TYPING':
return null;
}

return state;
Expand Down
Loading

0 comments on commit c970c16

Please sign in to comment.