aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/pdf/Annotation.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/pdf/Annotation.tsx')
-rw-r--r--src/client/views/pdf/Annotation.tsx168
1 files changed, 88 insertions, 80 deletions
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index a1f5ce703..7dd4047c1 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -1,24 +1,50 @@
import { action, computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { Id } from '../../../fields/FieldSymbols';
+import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc';
+import { Highlight } from '../../../fields/DocSymbols';
import { List } from '../../../fields/List';
-import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
-import { LinkFollower } from '../../util/LinkFollower';
+import { BoolCast, DocCast, NumCast, StrCast } from '../../../fields/Types';
import { LinkManager } from '../../util/LinkManager';
-import { undoBatch } from '../../util/UndoManager';
-import { OpenWhere } from '../nodes/DocumentView';
+import { undoable } from '../../util/UndoManager';
+import { ObservableReactComponent } from '../ObservableReactComponent';
+import { DocumentView } from '../nodes/DocumentView';
import { FieldViewProps } from '../nodes/FieldView';
+import { OpenWhere } from '../nodes/OpenWhere';
import { AnchorMenu } from './AnchorMenu';
import './Annotation.scss';
-import { ObservableReactComponent } from '../ObservableReactComponent';
+
+interface IRegionAnnotationProps {
+ x: number;
+ y: number;
+ width: number;
+ height: number;
+ opacity: () => number;
+ background: () => string;
+ outline: () => string | undefined;
+}
+
+const RegionAnnotation = function (props: IRegionAnnotationProps) {
+ return (
+ <div
+ className="htmlAnnotation"
+ style={{
+ left: NumCast(props.x),
+ top: NumCast(props.y),
+ width: NumCast(props.width),
+ height: NumCast(props.height),
+ opacity: props.opacity(),
+ outline: props.outline(),
+ backgroundColor: props.background(),
+ }}
+ />
+ );
+};
interface IAnnotationProps extends FieldViewProps {
- anno: Doc;
- dataDoc: Doc;
+ annoDoc: Doc;
+ containerDataDoc: Doc;
fieldKey: string;
- showInfo?: (anno: Opt<Doc>) => void;
pointerEvents?: () => Opt<string>;
}
@observer
@@ -27,62 +53,45 @@ export class Annotation extends ObservableReactComponent<IAnnotationProps> {
super(props);
makeObservable(this);
}
- render() {
- return (
- <div style={{ display: this._props.anno.textCopied && !Doc.GetBrushHighlightStatus(this._props.anno) ? 'none' : undefined }}>
- {DocListCast(this._props.anno.text_inlineAnnotations).map(a => (
- <RegionAnnotation pointerEvents={this._props.pointerEvents} {...this._props} document={a} key={a[Id]} />
- ))}
- </div>
- );
- }
-}
-interface IRegionAnnotationProps extends IAnnotationProps {
- document: Doc;
- pointerEvents?: () => Opt<string>;
-}
-@observer
-class RegionAnnotation extends ObservableReactComponent<IRegionAnnotationProps> {
- private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
-
- @computed get annoTextRegion() {
- return Cast(this._props.document.annoTextRegion, Doc, null) || this._props.document;
+ @computed get linkHighlighted() {
+ const found = LinkManager.Instance.getAllDirectLinks(this._props.annoDoc).find(link => {
+ const a1 = Doc.getOppositeAnchor(link, this._props.annoDoc);
+ return a1 && Doc.GetBrushStatus(DocCast(a1.annotationOn, a1));
+ });
+ return found;
}
- @undoBatch
- deleteAnnotation = () => {
- const docAnnotations = DocListCast(this._props.dataDoc[this._props.fieldKey]);
- this._props.dataDoc[this._props.fieldKey] = new List<Doc>(docAnnotations.filter(a => a !== this.annoTextRegion));
+ deleteAnnotation = undoable(() => {
+ const docAnnotations = DocListCast(this._props.containerDataDoc[this._props.fieldKey]);
+ this._props.containerDataDoc[this._props.fieldKey] = new List<Doc>(docAnnotations.filter(a => a !== this._props.annoDoc));
AnchorMenu.Instance.fadeOut(true);
this._props.select(false);
- };
+ }, 'delete annotation');
+
+ pinToPres = undoable(() => this._props.pinToPres(this._props.annoDoc, {}), 'pin to pres');
- @undoBatch
- pinToPres = () => this._props.pinToPres(this.annoTextRegion, {});
+ makeTargetToggle = undoable(() => { this._props.annoDoc.followLinkToggle = !this._props.annoDoc.followLinkToggle }, "set link toggle"); // prettier-ignore
- @undoBatch
- makeTargretToggle = () => (this.annoTextRegion.followLinkToggle = !this.annoTextRegion.followLinkToggle);
+ isTargetToggler = () => BoolCast(this._props.annoDoc.followLinkToggle);
- isTargetToggler = () => BoolCast(this.annoTextRegion.followLinkToggle);
- @undoBatch
- showTargetTrail = (anchor: Doc) => {
+ showTargetTrail = undoable((anchor: Doc) => {
const trail = DocCast(anchor.presentationTrail);
if (trail) {
Doc.ActivePresentation = trail;
this._props.addDocTab(trail, OpenWhere.replaceRight);
}
- };
+ }, 'show target trail');
@action
onContextMenu = (e: React.MouseEvent) => {
AnchorMenu.Instance.Status = 'annotation';
- AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this);
+ AnchorMenu.Instance.Delete = this.deleteAnnotation;
AnchorMenu.Instance.Pinned = false;
AnchorMenu.Instance.PinToPres = this.pinToPres;
- AnchorMenu.Instance.MakeTargetToggle = this.makeTargretToggle;
+ AnchorMenu.Instance.MakeTargetToggle = this.makeTargetToggle;
AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler;
- AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this.annoTextRegion);
+ AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this._props.annoDoc);
AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
e.stopPropagation();
e.preventDefault();
@@ -94,44 +103,43 @@ class RegionAnnotation extends ObservableReactComponent<IRegionAnnotationProps>
e.preventDefault();
} else if (e.button === 0) {
e.stopPropagation();
- LinkFollower.FollowLink(undefined, this.annoTextRegion, false);
+ DocumentView.FollowLink(undefined, this._props.annoDoc, false);
}
};
-
- @computed get linkHighlighted() {
- for (const link of LinkManager.Instance.getAllDirectLinks(this._props.document)) {
- const a1 = LinkManager.getOppositeAnchor(link, this._props.document);
- if (a1 && Doc.GetBrushStatus(DocCast(a1.annotationOn, this._props.document))) return true;
- }
- }
-
+ brushed = () => this._props.annoDoc && Doc.GetBrushHighlightStatus(this._props.annoDoc);
+ opacity = () => (this.brushed() === Doc.DocBrushStatus.highlighted ? 0.5 : 1);
+ outline = () => (this.linkHighlighted ? 'solid 1px lightBlue' : undefined);
+ background = () => (this._props.annoDoc[Highlight] ? 'orange' : StrCast(this._props.annoDoc.backgroundColor));
render() {
- const brushed = this.annoTextRegion && Doc.GetBrushHighlightStatus(this.annoTextRegion);
return (
- <div
- className="htmlAnnotation"
- ref={this._mainCont}
- onPointerEnter={action(() => {
- Doc.BrushDoc(this._props.anno);
- this._props.showInfo?.(this._props.anno);
- })}
- onPointerLeave={action(() => {
- Doc.UnBrushDoc(this._props.anno);
- this._props.showInfo?.(undefined);
- })}
- onPointerDown={this.onPointerDown}
- onContextMenu={this.onContextMenu}
- style={{
- left: NumCast(this._props.document.x),
- top: NumCast(this._props.document.y),
- width: NumCast(this._props.document._width),
- height: NumCast(this._props.document._height),
- opacity: brushed === Doc.DocBrushStatus.highlighted ? 0.5 : undefined,
- pointerEvents: this._props.pointerEvents?.() as any,
- outline: brushed === Doc.DocBrushStatus.unbrushed && this.linkHighlighted ? 'solid 1px lightBlue' : undefined,
- backgroundColor: brushed === Doc.DocBrushStatus.highlighted ? 'orange' : StrCast(this._props.document.backgroundColor),
- }}
- />
+ <div style={{ display: this._props.annoDoc.textCopied && !Doc.GetBrushHighlightStatus(this._props.annoDoc) ? 'none' : undefined }}>
+ {StrListCast(this._props.annoDoc.text_inlineAnnotations)
+ .map(a => a.split?.(':'))
+ .filter(fields => fields)
+ .map(([x, y, width, height]) => (
+ <div
+ key={'' + x + y + width + height}
+ style={{ pointerEvents: this._props.pointerEvents?.() as any }}
+ onPointerDown={this.onPointerDown}
+ onContextMenu={this.onContextMenu}
+ onPointerEnter={() => {
+ Doc.BrushDoc(this._props.annoDoc);
+ }}
+ onPointerLeave={() => {
+ Doc.UnBrushDoc(this._props.annoDoc);
+ }}>
+ <RegionAnnotation //
+ x={Number(x)}
+ y={Number(y)}
+ width={Number(width)}
+ height={Number(height)}
+ outline={this.outline}
+ background={this.background}
+ opacity={this.opacity}
+ />
+ </div>
+ ))}
+ </div>
);
}
}