Skip to content

Commit

Permalink
Create API to add formatters to Editable (remove footnotes from Edita…
Browse files Browse the repository at this point in the history
…ble)
  • Loading branch information
tg-ephox committed Oct 5, 2017
1 parent 26247a2 commit ece64ee
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 194 deletions.
78 changes: 33 additions & 45 deletions blocks/editable/format-toolbar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { Component } from '@wordpress/element';
import { Component, cloneElement } from '@wordpress/element';
import { IconButton, Toolbar, withSpokenMessages } from '@wordpress/components';
import { keycodes } from '@wordpress/utils';

Expand Down Expand Up @@ -46,8 +46,6 @@ class FormatToolbar extends Component {
settingsVisible: false,
opensInNewWindow: false,
newLinkValue: '',
showFootnoteEntry: false,
footnote: '',
};

this.addLink = this.addLink.bind( this );
Expand All @@ -59,9 +57,7 @@ class FormatToolbar extends Component {
this.toggleLinkSettingsVisibility = this.toggleLinkSettingsVisibility.bind( this );
this.setLinkTarget = this.setLinkTarget.bind( this );

this.addFootnote = this.addFootnote.bind( this );
this.onFootnoteChange = this.onFootnoteChange.bind( this );
this.submitFootnote = this.submitFootnote.bind( this );
this.getFormatterSettingsElement = this.getFormatterSettingsElement.bind( this );
}

componentDidMount() {
Expand Down Expand Up @@ -90,7 +86,6 @@ class FormatToolbar extends Component {
opensInNewWindow: !! nextProps.formats.link && !! nextProps.formats.link.target,
newLinkValue: '',
showFootnoteEntry: false,
foonote: '',
} );
}
}
Expand Down Expand Up @@ -139,30 +134,30 @@ class FormatToolbar extends Component {
}
}

onFootnoteChange( event ) {
this.setState( { footnote: event.target.value } );
}
getFormatterSettingsElement( formatters ) {
let settingsElement = null;
let formatterName = null;

addFootnote() {
this.setState( { showFootnoteEntry: true } );
}
for ( let formatter of formatters ) {
settingsElement = formatter.getSettingsElement();

submitFootnote( event ) {
event.preventDefault();
if ( !! settingsElement ) {
formatterName = formatter.name;
break;
}
}

this.props.onChange( { footnote: { text: this.state.footnote } } );
return settingsElement && cloneElement( settingsElement, { changeFormats: ( formatSettings ) => this.props.onChange( { [ formatterName ]: formatSettings } ) } );
}

render() {
const { formats, focusPosition, enabledControls = DEFAULT_CONTROLS } = this.props;
const { isAddingLink, isEditingLink, newLinkValue, settingsVisible, opensInNewWindow, showFootnoteEntry, footnote } = this.state;
const { formats, focusPosition, enabledControls = DEFAULT_CONTROLS, formatters = [] } = this.props;
const { isAddingLink, isEditingLink, newLinkValue, settingsVisible, opensInNewWindow } = this.state;
const linkStyle = focusPosition
? { position: 'absolute', ...focusPosition }
: null;

const isNodeFootnote = formats.link && formats.link.node.getAttribute( 'data-footnote-id' );

const toolbarControls = FORMATTING_CONTROLS
let toolbarControls = FORMATTING_CONTROLS
.filter( control => enabledControls.indexOf( control.format ) !== -1 )
.map( ( control ) => ( {
...control,
Expand All @@ -184,21 +179,29 @@ class FormatToolbar extends Component {
icon: 'admin-links',
title: __( 'Link' ),
onClick: this.addLink,
isActive: ( isAddingLink || !! formats.link ) && ! isNodeFootnote,
isActive: ( isAddingLink || !! formats.link ),
} );
}

toolbarControls.push( {
icon: 'format-audio',
title: __( 'Footnote' ),
onClick: this.addFootnote,
isActive: isNodeFootnote,
} );
toolbarControls = toolbarControls.concat( formatters );
const formatterSettings = this.getFormatterSettingsElement( formatters );

return (
<div className="blocks-format-toolbar">
<Toolbar controls={ toolbarControls } />
{ formatterSettings &&
<div className="blocks-format-toolbar__link-modal" style={ linkStyle }>
{ formatterSettings }
</div>
}
</div>
);
}
}

export default withSpokenMessages( FormatToolbar );

/*
{ ( isAddingLink || isEditingLink ) &&
<form
className="blocks-format-toolbar__link-modal"
Expand All @@ -212,7 +215,7 @@ class FormatToolbar extends Component {
</form>
}
{ ! isNodeFootnote && !! formats.link && ! isAddingLink && ! isEditingLink &&
{ !! formats.link && ! isAddingLink && ! isEditingLink &&
<div className="blocks-format-toolbar__link-modal" style={ linkStyle }>
<a
className="blocks-format-toolbar__link-value"
Expand All @@ -227,19 +230,4 @@ class FormatToolbar extends Component {
{ linkSettings }
</div>
}

{ ( isNodeFootnote || showFootnoteEntry ) &&
<form
style={ linkStyle }
className="blocks-format-toolbar__footnote-modal"
onSubmit={ this.submitFootnote }>
<textarea value={ footnote } onChange={ this.onFootnoteChange } placeholder={ __( 'Footnote' ) } />
<IconButton icon="yes" type="submit" label={ __( 'Apply' ) } />
</form>
}
</div>
);
}
}

export default withSpokenMessages( FormatToolbar );
*/
56 changes: 18 additions & 38 deletions blocks/editable/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ import {
find,
defer,
noop,
pick,
values,
} from 'lodash';
import { nodeListToReact } from 'dom-react';
import { Fill } from 'react-slot-fill';
import 'element-closest';
import uuid from 'uuid/v4';

/**
* WordPress dependencies
Expand Down Expand Up @@ -95,8 +94,6 @@ export default class Editable extends Component {
empty: ! value || ! value.length,
selectedNodeId: 0,
};

this.footnotes = props.footnotes || {};
}

getSettings( settings ) {
Expand Down Expand Up @@ -129,6 +126,10 @@ export default class Editable extends Component {
if ( this.props.onSetup ) {
this.props.onSetup( editor );
}

this.editorFacade = {
setNodeContent: ( content ) => editor.selection.setContent( content ),
};
}

proxyPropHandler( name ) {
Expand Down Expand Up @@ -237,34 +238,7 @@ export default class Editable extends Component {

this.savedContent = this.getContent();
this.editor.save();
this.footnotes = pick( this.footnotes, this.getFootnoteIds( this.savedContent ) );
this.props.onChange( { content: this.savedContent, footnotes: this.footnotes } );
}

getFootnoteIds( content ) {
const footnoteIds = [];
const nodesToVisit = [ ...content ];
let node = nodesToVisit.pop();

while ( node !== undefined ) {
if ( typeof node !== 'string' ) {
if ( node.type === 'a' && node.props[ 'data-footnote-id' ] ) {
footnoteIds.push( node.props[ 'data-footnote-id' ] );
}

if ( node.props.children ) {
if ( Array.isArray( node.props.children ) ) {
node.props.children.forEach( n => nodesToVisit.push( n ) );
} else {
nodesToVisit.push( node.props.children );
}
}
}

node = nodesToVisit.pop();
}

return footnoteIds;
this.props.onChange( this.savedContent );
}

getEditorSelectionRect() {
Expand Down Expand Up @@ -502,8 +476,11 @@ export default class Editable extends Component {
const activeFormats = this.editor.formatter.matchAll( [ 'bold', 'italic', 'strikethrough' ] );
activeFormats.forEach( ( activeFormat ) => formats[ activeFormat ] = true );

const selectedNodeId = this.state.selectedNodeId + 1;
forEach( this.props.formatters, formatter => formatter.onNodeChange( parents, selectedNodeId ) );

const focusPosition = this.getFocusPosition();
this.setState( { formats, focusPosition, selectedNodeId: this.state.selectedNodeId + 1 } );
this.setState( { parents, formats, focusPosition, selectedNodeId } );
}

updateContent() {
Expand Down Expand Up @@ -584,8 +561,12 @@ export default class Editable extends Component {
}

changeFormats( formats ) {
const { formatters } = this.props;

forEach( formats, ( formatValue, format ) => {
if ( format === 'link' ) {
if ( format in formatters ) {
formatters[ format ].applyFormat( this.editorFacade, formatValue );
} else if ( format === 'link' ) {
if ( formatValue !== undefined ) {
const anchor = this.editor.dom.getParent( this.editor.selection.getNode(), 'a' );
if ( ! anchor ) {
Expand All @@ -595,10 +576,6 @@ export default class Editable extends Component {
} else {
this.editor.execCommand( 'Unlink' );
}
} else if ( format === 'footnote' ) {
const footnoteId = uuid();
this.footnotes[ footnoteId ] = formatValue.text;
this.editor.selection.setContent( `<a href="#footnote-${ footnoteId }" data-footnote-id="${ footnoteId }" contenteditable="false"><sup>[*]</sup></a>` );
} else {
const isActive = this.isFormatActive( format );
if ( isActive && ! formatValue ) {
Expand All @@ -609,6 +586,7 @@ export default class Editable extends Component {
}
} );

// TODO: investigate whether formatter format needs to be in here
this.setState( ( state ) => ( {
formats: merge( {}, state.formats, formats ),
} ) );
Expand All @@ -629,6 +607,7 @@ export default class Editable extends Component {
placeholder,
multiline: MultilineTag,
keepPlaceholderOnFocus = false,
formatters,
} = this.props;

// Generating a key that includes `tagName` ensures that if the tag
Expand All @@ -645,6 +624,7 @@ export default class Editable extends Component {
formats={ this.state.formats }
onChange={ this.changeFormats }
enabledControls={ formattingControls }
formatters={ values( formatters ) }
/>
);

Expand Down
Loading

0 comments on commit ece64ee

Please sign in to comment.