Polyfills to enable editor positoning on MS Edge (issue #7)
This commit is contained in:
parent
a22068960a
commit
41529a4cd1
|
@ -2,9 +2,11 @@ import React, { Component } from 'preact/compat';
|
||||||
import Editor from './editor/Editor';
|
import Editor from './editor/Editor';
|
||||||
import Highlighter from './highlighter/Highlighter';
|
import Highlighter from './highlighter/Highlighter';
|
||||||
import SelectionHandler from './selection/SelectionHandler';
|
import SelectionHandler from './selection/SelectionHandler';
|
||||||
import RelationsLayer from './relations/RelationsLayer';
|
import RelationsLayer from './relations/RelationsLayer';
|
||||||
import RelationEditor from './relations/editor/RelationEditor';
|
import RelationEditor from './relations/editor/RelationEditor';
|
||||||
|
|
||||||
|
import './utils/MSEdgePolyfills';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pulls the strings between the annotation highlight layer
|
* Pulls the strings between the annotation highlight layer
|
||||||
* and the editor popup.
|
* and the editor popup.
|
||||||
|
@ -53,18 +55,18 @@ export default class TextAnnotator extends Component {
|
||||||
document.removeEventListener('keydown', this.handleEscape);
|
document.removeEventListener('keydown', this.handleEscape);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************/
|
/**************************/
|
||||||
/* Annotation CRUD events */
|
/* Annotation CRUD events */
|
||||||
/**************************/
|
/**************************/
|
||||||
|
|
||||||
/** Selection on the text **/
|
/** Selection on the text **/
|
||||||
handleSelect = evt => {
|
handleSelect = evt => {
|
||||||
const { selection, clientRect } = evt;
|
const { selection, clientRect } = evt;
|
||||||
if (selection) {
|
if (selection) {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedAnnotation: null,
|
selectedAnnotation: null,
|
||||||
selectionBounds: null
|
selectionBounds: null
|
||||||
}, () => this.setState({
|
}, () => this.setState({
|
||||||
selectedAnnotation: selection,
|
selectedAnnotation: selection,
|
||||||
selectionBounds: clientRect
|
selectionBounds: clientRect
|
||||||
}))
|
}))
|
||||||
|
@ -76,12 +78,12 @@ export default class TextAnnotator extends Component {
|
||||||
/**
|
/**
|
||||||
* A convenience method that allows the external application to
|
* A convenience method that allows the external application to
|
||||||
* override the autogenerated Id for an annotation.
|
* override the autogenerated Id for an annotation.
|
||||||
*
|
*
|
||||||
* Usually, the override will happen almost immediately after
|
* Usually, the override will happen almost immediately after
|
||||||
* the annotation is created. But we need to be defensive and assume
|
* the annotation is created. But we need to be defensive and assume
|
||||||
* that the override might come in with considerable delay, thus
|
* that the override might come in with considerable delay, thus
|
||||||
* the user might have made further edits already.
|
* the user might have made further edits already.
|
||||||
*
|
*
|
||||||
* A key challenge here is that there may be dependencies between
|
* A key challenge here is that there may be dependencies between
|
||||||
* the original annotation and relations that were created meanwhile.
|
* the original annotation and relations that were created meanwhile.
|
||||||
*/
|
*/
|
||||||
|
@ -113,14 +115,14 @@ export default class TextAnnotator extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenience method that allows the external application to
|
* A convenience method that allows the external application to
|
||||||
* override the autogenerated Id for a relation.
|
* override the autogenerated Id for a relation.
|
||||||
*
|
*
|
||||||
* This operation is less problematic than .overrideAnnotation().
|
* This operation is less problematic than .overrideAnnotation().
|
||||||
* We just need to make sure the RelationEditor is closed, so that
|
* We just need to make sure the RelationEditor is closed, so that
|
||||||
* the annotation doesn't become orphaned. Otherwise, there are
|
* the annotation doesn't become orphaned. Otherwise, there are
|
||||||
* no dependencies.
|
* no dependencies.
|
||||||
*/
|
*/
|
||||||
overrideRelationId = originalId => forcedId => {
|
overrideRelationId = originalId => forcedId => {
|
||||||
if (this.state.selectedRelation) {
|
if (this.state.selectedRelation) {
|
||||||
|
@ -134,14 +136,14 @@ export default class TextAnnotator extends Component {
|
||||||
/** Common handler for annotation CREATE or UPDATE **/
|
/** Common handler for annotation CREATE or UPDATE **/
|
||||||
onCreateOrUpdateAnnotation = method => (annotation, previous) => {
|
onCreateOrUpdateAnnotation = method => (annotation, previous) => {
|
||||||
this.clearState();
|
this.clearState();
|
||||||
|
|
||||||
this.selectionHandler.clearSelection();
|
this.selectionHandler.clearSelection();
|
||||||
this.highlighter.addOrUpdateAnnotation(annotation, previous);
|
this.highlighter.addOrUpdateAnnotation(annotation, previous);
|
||||||
|
|
||||||
// Call CREATE or UPDATE handler
|
// Call CREATE or UPDATE handler
|
||||||
if (previous)
|
if (previous)
|
||||||
this.props[method](annotation.clone(), previous.clone());
|
this.props[method](annotation.clone(), previous.clone());
|
||||||
else
|
else
|
||||||
this.props[method](annotation.clone(), this.overrideAnnotationId(annotation));
|
this.props[method](annotation.clone(), this.overrideAnnotationId(annotation));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,9 +164,9 @@ export default class TextAnnotator extends Component {
|
||||||
this.selectionHandler.clearSelection();
|
this.selectionHandler.clearSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************/
|
/************************/
|
||||||
/* Relation CRUD events */
|
/* Relation CRUD events */
|
||||||
/************************/
|
/************************/
|
||||||
|
|
||||||
// Shorthand
|
// Shorthand
|
||||||
closeRelationsEditor = () => {
|
closeRelationsEditor = () => {
|
||||||
|
@ -177,7 +179,7 @@ export default class TextAnnotator extends Component {
|
||||||
* or newly created connection for editing.
|
* or newly created connection for editing.
|
||||||
*/
|
*/
|
||||||
onEditRelation = relation => {
|
onEditRelation = relation => {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedRelation: relation
|
selectedRelation: relation
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -205,9 +207,9 @@ export default class TextAnnotator extends Component {
|
||||||
this.props.onAnnotationDeleted(relation.annotation);
|
this.props.onAnnotationDeleted(relation.annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************/
|
/****************/
|
||||||
/* External API */
|
/* External API */
|
||||||
/****************/
|
/****************/
|
||||||
|
|
||||||
addAnnotation = annotation => {
|
addAnnotation = annotation => {
|
||||||
this.highlighter.addOrUpdateAnnotation(annotation.clone());
|
this.highlighter.addOrUpdateAnnotation(annotation.clone());
|
||||||
|
@ -223,7 +225,7 @@ export default class TextAnnotator extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
setAnnotations = annotations => {
|
setAnnotations = annotations => {
|
||||||
const clones = annotations.map(a => a.clone());
|
const clones = annotations.map(a => a.clone());
|
||||||
this.highlighter.init(clones).then(() =>
|
this.highlighter.init(clones).then(() =>
|
||||||
this.relationsLayer.init(clones));
|
this.relationsLayer.init(clones));
|
||||||
}
|
}
|
||||||
|
@ -237,14 +239,14 @@ export default class TextAnnotator extends Component {
|
||||||
setMode = mode => {
|
setMode = mode => {
|
||||||
if (mode === 'RELATIONS') {
|
if (mode === 'RELATIONS') {
|
||||||
this.clearState();
|
this.clearState();
|
||||||
|
|
||||||
this.selectionHandler.enabled = false;
|
this.selectionHandler.enabled = false;
|
||||||
|
|
||||||
this.relationsLayer.readOnly = false;
|
this.relationsLayer.readOnly = false;
|
||||||
this.relationsLayer.startDrawing();
|
this.relationsLayer.startDrawing();
|
||||||
} else {
|
} else {
|
||||||
this.setState({ selectedRelation: null });
|
this.setState({ selectedRelation: null });
|
||||||
|
|
||||||
this.selectionHandler.enabled = true;
|
this.selectionHandler.enabled = true;
|
||||||
|
|
||||||
this.relationsLayer.readOnly = true;
|
this.relationsLayer.readOnly = true;
|
||||||
|
@ -252,7 +254,7 @@ export default class TextAnnotator extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applyTemplate = (bodies, headless) =>
|
applyTemplate = (bodies, headless) =>
|
||||||
this.setState({ applyTemplate: bodies, headless })
|
this.setState({ applyTemplate: bodies, headless })
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -276,17 +278,17 @@ export default class TextAnnotator extends Component {
|
||||||
</Editor>
|
</Editor>
|
||||||
}
|
}
|
||||||
|
|
||||||
{ this.state.selectedRelation &&
|
{ this.state.selectedRelation &&
|
||||||
<RelationEditor
|
<RelationEditor
|
||||||
relation={this.state.selectedRelation}
|
relation={this.state.selectedRelation}
|
||||||
onRelationCreated={this.onCreateOrUpdateRelation}
|
onRelationCreated={this.onCreateOrUpdateRelation}
|
||||||
onRelationUpdated={this.onCreateOrUpdateRelation}
|
onRelationUpdated={this.onCreateOrUpdateRelation}
|
||||||
onRelationDeleted={this.onDeleteRelation}
|
onRelationDeleted={this.onDeleteRelation}
|
||||||
onCancel={this.closeRelationsEditor}
|
onCancel={this.closeRelationsEditor}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,9 @@ const setPosition = (wrapperEl, editorEl, annotationBounds) => {
|
||||||
editorEl.style.opacity = 1;
|
editorEl.style.opacity = 1;
|
||||||
|
|
||||||
// Default orientation
|
// Default orientation
|
||||||
const { x, y, height, top, right } = annotationBounds;
|
const { left, top, right, height } = annotationBounds;
|
||||||
editorEl.style.top = `${y + height - containerBounds.top}px`;
|
editorEl.style.top = `${top + height - containerBounds.top}px`;
|
||||||
editorEl.style.left = `${x + scrollX - containerBounds.left}px`;
|
editorEl.style.left = `${left + scrollX - containerBounds.left}px`;
|
||||||
|
|
||||||
const defaultOrientation = editorEl.getBoundingClientRect();
|
const defaultOrientation = editorEl.getBoundingClientRect();
|
||||||
|
|
||||||
|
@ -27,11 +27,11 @@ const setPosition = (wrapperEl, editorEl, annotationBounds) => {
|
||||||
// Flip vertically
|
// Flip vertically
|
||||||
const annotationTop = top + scrollY; // Annotation top relative to parents
|
const annotationTop = top + scrollY; // Annotation top relative to parents
|
||||||
const containerHeight = containerBounds.height + containerBounds.top + scrollY;
|
const containerHeight = containerBounds.height + containerBounds.top + scrollY;
|
||||||
|
|
||||||
editorEl.classList.add('align-bottom');
|
editorEl.classList.add('align-bottom');
|
||||||
editorEl.style.top = 'auto';
|
editorEl.style.top = 'auto';
|
||||||
editorEl.style.bottom = `${containerHeight - annotationTop}px`;
|
editorEl.style.bottom = `${containerHeight - annotationTop}px`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default setPosition;
|
export default setPosition;
|
||||||
|
|
|
@ -17,7 +17,7 @@ export default class SelectionHandler extends EventEmitter {
|
||||||
element.addEventListener('mouseup', this._onMouseUp);
|
element.addEventListener('mouseup', this._onMouseUp);
|
||||||
|
|
||||||
if (IS_TOUCH)
|
if (IS_TOUCH)
|
||||||
enableTouch(element, this._onMouseUp);
|
enableTouch(element, this._onMouseUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
get enabled() {
|
get enabled() {
|
||||||
|
@ -39,9 +39,9 @@ export default class SelectionHandler extends EventEmitter {
|
||||||
if (selection.isCollapsed) {
|
if (selection.isCollapsed) {
|
||||||
const annotationSpan = evt.target.closest('.r6o-annotation');
|
const annotationSpan = evt.target.closest('.r6o-annotation');
|
||||||
if (annotationSpan) {
|
if (annotationSpan) {
|
||||||
this.emit('select', {
|
this.emit('select', {
|
||||||
selection: this.highlighter.getAnnotationsAt(annotationSpan)[0],
|
selection: this.highlighter.getAnnotationsAt(annotationSpan)[0],
|
||||||
clientRect: annotationSpan.getBoundingClientRect()
|
clientRect: annotationSpan.getBoundingClientRect()
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// De-select
|
// De-select
|
||||||
|
@ -92,4 +92,4 @@ export default class SelectionHandler extends EventEmitter {
|
||||||
this.el.normalize();
|
this.el.normalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
if (!Element.prototype.matches) {
|
||||||
|
Element.prototype.matches = Element.prototype.msMatchesSelector ||
|
||||||
|
Element.prototype.webkitMatchesSelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Element.prototype.closest) {
|
||||||
|
Element.prototype.closest = function(s) {
|
||||||
|
let el = this;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (Element.prototype.matches.call(el, s)) return el;
|
||||||
|
el = el.parentElement || el.parentNode;
|
||||||
|
} while (el !== null && el.nodeType === 1);
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue