-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement a basic text annotation proof of concept
- Loading branch information
Showing
8 changed files
with
386 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
import { Component, Fragment } from '@wordpress/element'; | ||
import { isEqual, map } from 'lodash'; | ||
|
||
class Annotation extends Component { | ||
constructor( props ) { | ||
super( props ); | ||
|
||
this.state = { | ||
prevAnnotation: this.props.annotation, | ||
isDirty: true, | ||
positions: [], | ||
}; | ||
|
||
this.calculatePosition = this.calculatePosition.bind( this ); | ||
} | ||
|
||
matchEditorXPath( xpath ) { | ||
const body = this.props.editor.getBody(); | ||
|
||
const results = document.evaluate( | ||
xpath, | ||
body, | ||
null, | ||
XPathResult.UNORDERED_NODE_ITERATOR_TYPE | ||
); | ||
const firstResult = results.iterateNext(); | ||
if ( firstResult ) { | ||
return firstResult; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
createRange( startNode, endNode, startOffset, endOffset ) { | ||
const range = document.createRange(); | ||
|
||
range.setStart( startNode, startOffset ); | ||
range.setEnd( endNode, endOffset ); | ||
|
||
return range; | ||
} | ||
|
||
calculatePosition() { | ||
const { annotation } = this.props; | ||
|
||
const { | ||
startXPath, | ||
startOffset, | ||
endXPath, | ||
endOffset, | ||
} = annotation; | ||
|
||
let startNode, endNode; | ||
|
||
try { | ||
startNode = this.matchEditorXPath( startXPath ); | ||
endNode = this.matchEditorXPath( endXPath ); | ||
} catch ( e ) { | ||
return; | ||
} | ||
|
||
const range = this.createRange( startNode, endNode, startOffset, endOffset ); | ||
const editorRect = this.props.containerRef.current.getBoundingClientRect(); | ||
|
||
const positions = map( range.getClientRects(), ( rect ) => { | ||
return { | ||
width: rect.width, | ||
height: rect.height, | ||
top: rect.top - editorRect.top, | ||
left: rect.left - editorRect.left, | ||
}; | ||
} ); | ||
|
||
this.setState( { | ||
positions, | ||
prevAnnotation: this.props.annotation, | ||
isDirty: false, | ||
} ); | ||
} | ||
|
||
updatePosition() { | ||
if ( this.state.isDirty ) { | ||
// Makes sure that this is not done synchronously on the same frame. | ||
setTimeout( this.calculatePosition, 10 ); | ||
} | ||
} | ||
|
||
componentDidMount() { | ||
this.updatePosition(); | ||
} | ||
|
||
componentDidUpdate() { | ||
this.updatePosition(); | ||
} | ||
|
||
static getDerivedStateFromProps( props, state ) { | ||
const isDirty = ! isEqual( props.annotation, state.prevAnnotation ); | ||
|
||
return { | ||
isDirty, | ||
}; | ||
} | ||
|
||
render() { | ||
const { annotation } = this.props; | ||
|
||
return <Fragment> | ||
<div className="annotation-marker-debug"> | ||
{ JSON.stringify( this.props.annotation ) } | ||
</div> | ||
{ this.state.positions.map( ( position, index ) => { | ||
let { | ||
width, | ||
height, | ||
top, | ||
left, | ||
} = position; | ||
|
||
width = width + 'px'; | ||
height = height + 'px'; | ||
top = top + 'px'; | ||
left = left + 'px'; | ||
|
||
return <div | ||
className="annotation-marker" | ||
key={ annotation.id + '-' + index } | ||
style={ { width, height, top, left } } | ||
/>; | ||
} ) } | ||
</Fragment>; | ||
} | ||
} | ||
|
||
export default Annotation; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { Component } from '@wordpress/element'; | ||
import Annotation from './annotation'; | ||
|
||
class Annotations extends Component { | ||
render() { | ||
const { annotations } = this.props; | ||
|
||
// Don't try to render annotations while we don't have an editor. | ||
if ( ! this.props.editor ) { | ||
return null; | ||
} | ||
|
||
return <div className="annotation-markers"> | ||
{ annotations.map( ( annotation ) => { | ||
return <Annotation | ||
key={ annotation.id } | ||
annotation={ annotation } | ||
containerRef={ this.props.containerRef } | ||
editor={ this.props.editor } | ||
/>; | ||
} ) } | ||
</div>; | ||
} | ||
} | ||
|
||
export default Annotations; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.