diff options
Diffstat (limited to 'src/client/views/pdf/Annotation.tsx')
-rw-r--r-- | src/client/views/pdf/Annotation.tsx | 168 |
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> ); } } |