Polyfills to enable editor positoning on MS Edge (issue #7)

This commit is contained in:
Simon Rainer 2020-05-05 13:13:10 +02:00
parent a22068960a
commit 41529a4cd1
4 changed files with 54 additions and 36 deletions

View File

@ -2,9 +2,11 @@ import React, { Component } from 'preact/compat';
import Editor from './editor/Editor';
import Highlighter from './highlighter/Highlighter';
import SelectionHandler from './selection/SelectionHandler';
import RelationsLayer from './relations/RelationsLayer';
import RelationsLayer from './relations/RelationsLayer';
import RelationEditor from './relations/editor/RelationEditor';
import './utils/MSEdgePolyfills';
/**
* Pulls the strings between the annotation highlight layer
* and the editor popup.
@ -53,18 +55,18 @@ export default class TextAnnotator extends Component {
document.removeEventListener('keydown', this.handleEscape);
}
/**************************/
/**************************/
/* Annotation CRUD events */
/**************************/
/**************************/
/** Selection on the text **/
handleSelect = evt => {
const { selection, clientRect } = evt;
if (selection) {
this.setState({
selectedAnnotation: null,
selectionBounds: null
}, () => this.setState({
this.setState({
selectedAnnotation: null,
selectionBounds: null
}, () => this.setState({
selectedAnnotation: selection,
selectionBounds: clientRect
}))
@ -76,12 +78,12 @@ export default class TextAnnotator extends Component {
/**
* A convenience method that allows the external application to
* override the autogenerated Id for an annotation.
*
*
* Usually, the override will happen almost immediately after
* the annotation is created. But we need to be defensive and assume
* that the override might come in with considerable delay, thus
* the user might have made further edits already.
*
*
* A key challenge here is that there may be dependencies between
* 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
* override the autogenerated Id for a relation.
*
*
* This operation is less problematic than .overrideAnnotation().
* We just need to make sure the RelationEditor is closed, so that
* the annotation doesn't become orphaned. Otherwise, there are
* no dependencies.
* no dependencies.
*/
overrideRelationId = originalId => forcedId => {
if (this.state.selectedRelation) {
@ -134,14 +136,14 @@ export default class TextAnnotator extends Component {
/** Common handler for annotation CREATE or UPDATE **/
onCreateOrUpdateAnnotation = method => (annotation, previous) => {
this.clearState();
this.selectionHandler.clearSelection();
this.highlighter.addOrUpdateAnnotation(annotation, previous);
// Call CREATE or UPDATE handler
if (previous)
this.props[method](annotation.clone(), previous.clone());
else
else
this.props[method](annotation.clone(), this.overrideAnnotationId(annotation));
}
@ -162,9 +164,9 @@ export default class TextAnnotator extends Component {
this.selectionHandler.clearSelection();
}
/************************/
/************************/
/* Relation CRUD events */
/************************/
/************************/
// Shorthand
closeRelationsEditor = () => {
@ -177,7 +179,7 @@ export default class TextAnnotator extends Component {
* or newly created connection for editing.
*/
onEditRelation = relation => {
this.setState({
this.setState({
selectedRelation: relation
});
}
@ -205,9 +207,9 @@ export default class TextAnnotator extends Component {
this.props.onAnnotationDeleted(relation.annotation);
}
/****************/
/****************/
/* External API */
/****************/
/****************/
addAnnotation = annotation => {
this.highlighter.addOrUpdateAnnotation(annotation.clone());
@ -223,7 +225,7 @@ export default class TextAnnotator extends Component {
}
setAnnotations = annotations => {
const clones = annotations.map(a => a.clone());
const clones = annotations.map(a => a.clone());
this.highlighter.init(clones).then(() =>
this.relationsLayer.init(clones));
}
@ -237,14 +239,14 @@ export default class TextAnnotator extends Component {
setMode = mode => {
if (mode === 'RELATIONS') {
this.clearState();
this.selectionHandler.enabled = false;
this.relationsLayer.readOnly = false;
this.relationsLayer.startDrawing();
} else {
this.setState({ selectedRelation: null });
this.selectionHandler.enabled = 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 })
render() {
@ -276,17 +278,17 @@ export default class TextAnnotator extends Component {
</Editor>
}
{ this.state.selectedRelation &&
<RelationEditor
{ this.state.selectedRelation &&
<RelationEditor
relation={this.state.selectedRelation}
onRelationCreated={this.onCreateOrUpdateRelation}
onRelationUpdated={this.onCreateOrUpdateRelation}
onRelationDeleted={this.onDeleteRelation}
onCancel={this.closeRelationsEditor}
/>
/>
}
</>
);
}
}
}

View File

@ -11,9 +11,9 @@ const setPosition = (wrapperEl, editorEl, annotationBounds) => {
editorEl.style.opacity = 1;
// Default orientation
const { x, y, height, top, right } = annotationBounds;
editorEl.style.top = `${y + height - containerBounds.top}px`;
editorEl.style.left = `${x + scrollX - containerBounds.left}px`;
const { left, top, right, height } = annotationBounds;
editorEl.style.top = `${top + height - containerBounds.top}px`;
editorEl.style.left = `${left + scrollX - containerBounds.left}px`;
const defaultOrientation = editorEl.getBoundingClientRect();
@ -27,11 +27,11 @@ const setPosition = (wrapperEl, editorEl, annotationBounds) => {
// Flip vertically
const annotationTop = top + scrollY; // Annotation top relative to parents
const containerHeight = containerBounds.height + containerBounds.top + scrollY;
editorEl.classList.add('align-bottom');
editorEl.style.top = 'auto';
editorEl.style.bottom = `${containerHeight - annotationTop}px`;
}
}
export default setPosition;
export default setPosition;

View File

@ -17,7 +17,7 @@ export default class SelectionHandler extends EventEmitter {
element.addEventListener('mouseup', this._onMouseUp);
if (IS_TOUCH)
enableTouch(element, this._onMouseUp);
enableTouch(element, this._onMouseUp);
}
get enabled() {
@ -39,9 +39,9 @@ export default class SelectionHandler extends EventEmitter {
if (selection.isCollapsed) {
const annotationSpan = evt.target.closest('.r6o-annotation');
if (annotationSpan) {
this.emit('select', {
this.emit('select', {
selection: this.highlighter.getAnnotationsAt(annotationSpan)[0],
clientRect: annotationSpan.getBoundingClientRect()
clientRect: annotationSpan.getBoundingClientRect()
});
} else {
// De-select
@ -92,4 +92,4 @@ export default class SelectionHandler extends EventEmitter {
this.el.normalize();
}
}
}

View File

@ -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;
};
}