aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/pdf
diff options
context:
space:
mode:
authorsrichman333 <sarah_n_richman@brown.edu>2023-11-24 17:59:13 -0500
committersrichman333 <sarah_n_richman@brown.edu>2023-11-24 17:59:13 -0500
commit0b38b0629496973d6c4571208710096deb91b7d7 (patch)
treef797da626587c198535c0ea54aee9d467226262a /src/client/views/pdf
parent1b412d402c77a2aae82cf86b1f6a23f8a4f82caf (diff)
merge
Diffstat (limited to 'src/client/views/pdf')
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx2
-rw-r--r--src/client/views/pdf/Annotation.tsx36
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.tsx25
-rw-r--r--src/client/views/pdf/PDFViewer.tsx63
4 files changed, 71 insertions, 55 deletions
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 18cf633f4..d68859e88 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -64,7 +64,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
componentDidMount() {
this._disposer = reaction(
() => SelectionManager.Views().slice(),
- selected => AnchorMenu.Instance.fadeOut(true)
+ sel => AnchorMenu.Instance.fadeOut(true)
);
}
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 52904b852..e1e87763c 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -12,19 +12,20 @@ import { FieldViewProps } from '../nodes/FieldView';
import { AnchorMenu } from './AnchorMenu';
import './Annotation.scss';
import { LinkManager } from '../../util/LinkManager';
+import { Rect } from 'react-measure';
interface IAnnotationProps extends FieldViewProps {
anno: Doc;
dataDoc: Doc;
fieldKey: string;
- showInfo: (anno: Opt<Doc>) => void;
+ showInfo?: (anno: Opt<Doc>) => void;
pointerEvents?: () => Opt<string>;
}
@observer
export class Annotation extends React.Component<IAnnotationProps> {
render() {
return (
- <div style={{ display: this.props.anno.textCopied && !Doc.isBrushedHighlightedDegree(this.props.anno) ? 'none' : undefined }}>
+ <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]} />
))}
@@ -70,17 +71,23 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
};
@action
+ onContextMenu = (e: React.MouseEvent) => {
+ AnchorMenu.Instance.Status = 'annotation';
+ AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this);
+ AnchorMenu.Instance.Pinned = false;
+ AnchorMenu.Instance.PinToPres = this.pinToPres;
+ AnchorMenu.Instance.MakeTargetToggle = this.makeTargretToggle;
+ AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler;
+ AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this.annoTextRegion);
+ AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
+ e.stopPropagation();
+ e.preventDefault();
+ };
+ @action
onPointerDown = (e: React.PointerEvent) => {
if (e.button === 2 || e.ctrlKey) {
- AnchorMenu.Instance.Status = 'annotation';
- AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this);
- AnchorMenu.Instance.Pinned = false;
- AnchorMenu.Instance.PinToPres = this.pinToPres;
- AnchorMenu.Instance.MakeTargetToggle = this.makeTargretToggle;
- AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler;
- AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this.annoTextRegion);
- AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
e.stopPropagation();
+ e.preventDefault();
} else if (e.button === 0) {
e.stopPropagation();
LinkFollower.FollowLink(undefined, this.annoTextRegion, false);
@@ -90,25 +97,26 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
@computed get linkHighlighted() {
for (const link of LinkManager.Instance.getAllDirectLinks(this.props.document)) {
const a1 = LinkManager.getOppositeAnchor(link, this.props.document);
- if (a1 && Doc.IsBrushedDegreeUnmemoized(DocCast(a1.annotationOn, this.props.document))) return true;
+ if (a1 && Doc.GetBrushStatus(DocCast(a1.annotationOn, this.props.document))) return true;
}
}
render() {
- const brushed = this.annoTextRegion && Doc.isBrushedHighlightedDegree(this.annoTextRegion);
+ 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);
+ this.props.showInfo?.(this.props.anno);
})}
onPointerLeave={action(() => {
Doc.UnBrushDoc(this.props.anno);
- this.props.showInfo(undefined);
+ this.props.showInfo?.(undefined);
})}
onPointerDown={this.onPointerDown}
+ onContextMenu={this.onContextMenu}
style={{
left: NumCast(this.props.document.x),
top: NumCast(this.props.document.y),
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 038c45582..5392ec88e 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -211,6 +211,31 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
);
};
+ data = () => {
+ return (
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
+ {this.heading('GENERATED IMAGE')}
+ <div className="image-content-wrapper">
+ {this.imgUrls.map(rawSrc => (
+ <div className="img-wrapper">
+ <div className="img-container">
+ <img key={rawSrc[0]} src={rawSrc[0]} width={150} height={150} alt="dalle generation" />
+ </div>
+ <div className="btn-container">
+ <Button text="Save Image" onClick={() => this.transferToImage(rawSrc[1])} color={StrCast(Doc.UserDoc().userColor)} type={Type.TERT} />
+ </div>
+ </div>
+ ))}
+ </div>
+ {!this.loading && (
+ <>
+ <IconButton tooltip="Generate Again" onClick={this.generateImage} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
+ </>
+ )}
+ </div>
+ );
+ };
+
summaryBox = () => (
<>
<div>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 43b662f0f..ca9bf7bd2 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -2,7 +2,7 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio
import { observer } from 'mobx-react';
import * as Pdfjs from 'pdfjs-dist';
import 'pdfjs-dist/web/pdf_viewer.css';
-import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { Height } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
@@ -11,7 +11,6 @@ import { TraceMobx } from '../../../fields/util';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, returnAll, returnFalse, returnNone, returnZero, smoothScroll, Utils } from '../../../Utils';
import { DocUtils } from '../../documents/Documents';
import { SelectionManager } from '../../util/SelectionManager';
-import { SharingManager } from '../../util/SharingManager';
import { SnappingManager } from '../../util/SnappingManager';
import { MarqueeOptionsMenu } from '../collections/collectionFreeForm';
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
@@ -56,16 +55,15 @@ export class PDFViewer extends React.Component<IViewerProps> {
static _annotationStyle: any = addStyleSheet();
@observable private _pageSizes: { width: number; height: number }[] = [];
@observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
- @observable private _marqueeing: number[] | undefined;
@observable private _textSelecting = true;
@observable private _showWaiting = true;
- @observable private _overlayAnnoInfo: Opt<Doc>;
@observable private Index: number = -1;
private _pdfViewer: any;
private _styleRule: any; // stylesheet rule for making hyperlinks clickable
private _retries = 0; // number of times tried to create the PDF viewer
private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
+ private _marqueeref = React.createRef<MarqueeAnnotator>();
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
private _disposers: { [name: string]: IReactionDisposer } = {};
private _viewer: React.RefObject<HTMLDivElement> = React.createRef();
@@ -110,13 +108,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
this._disposers.selected = reaction(
() => this.props.isSelected(),
- selected => {
- // if (!selected) {
- // Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
- // Array.from(this._savedAnnotations.keys()).forEach(k => this._savedAnnotations.set(k, []));
- // }
- SelectionManager.Views().length === 1 && this.setupPdfJsViewer();
- },
+ selected => SelectionManager.Views().length === 1 && this.setupPdfJsViewer(),
{ fireImmediately: true }
);
this._disposers.curPage = reaction(
@@ -374,17 +366,14 @@ export class PDFViewer extends React.Component<IViewerProps> {
if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
this.props.select(false);
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- this._marqueeing = [e.clientX, e.clientY];
+ this._marqueeref.current?.onInitiateSelection([e.clientX, e.clientY]);
this.isAnnotating = true;
const target = e.target as any;
if (e.target && (target.className.includes('endOfContent') || (target.parentElement.className !== 'textLayer' && target.parentElement.parentElement?.className !== 'textLayer'))) {
this._textSelecting = false;
} else {
// if textLayer is hit, then we select text instead of using a marquee so clear out the marquee.
- setTimeout(
- action(() => (this._marqueeing = undefined)),
- 100
- ); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
+ setTimeout(() => this._marqueeref.current?.onTerminateSelection(), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, 'htmlAnnotation', { 'pointer-events': 'none' });
document.addEventListener('pointerup', this.onSelectEnd);
@@ -396,12 +385,13 @@ export class PDFViewer extends React.Component<IViewerProps> {
finishMarquee = (x?: number, y?: number) => {
this._getAnchor = AnchorMenu.Instance?.GetAnchor;
this.isAnnotating = false;
- this._marqueeing = undefined;
+ this._marqueeref.current?.onTerminateSelection();
this._textSelecting = true;
};
@action
onSelectEnd = (e: PointerEvent): void => {
+ this._getAnchor = AnchorMenu.Instance?.GetAnchor;
this.isAnnotating = false;
clearStyleSheetRules(PDFViewer._annotationStyle);
this.props.select(false);
@@ -425,23 +415,26 @@ export class PDFViewer extends React.Component<IViewerProps> {
@action
createTextAnnotation = (sel: Selection, selRange: Range) => {
if (this._mainCont.current) {
+ this._mainCont.current.style.transform = `rotate(${NumCast(this.props.DocumentView!().screenToLocalTransform().RotateDeg)}deg)`;
const boundingRect = this._mainCont.current.getBoundingClientRect();
const clientRects = selRange.getClientRects();
for (let i = 0; i < clientRects.length; i++) {
const rect = clientRects.item(i);
- if (rect?.width && rect.width < this._mainCont.current.clientWidth / this.props.ScreenToLocalTransform().Scale) {
+ if (rect && rect?.width && rect.width < this._mainCont.current.clientWidth / this.props.ScreenToLocalTransform().Scale) {
const scaleX = this._mainCont.current.offsetWidth / boundingRect.width;
+ const scaleY = this._mainCont.current.offsetHeight / boundingRect.height;
const pdfScale = NumCast(this.props.layoutDoc._freeform_scale, 1);
const annoBox = document.createElement('div');
annoBox.className = 'marqueeAnnotator-annotationBox';
// transforms the positions from screen onto the pdf div
- annoBox.style.top = (((rect.top - boundingRect.top) * scaleX) / pdfScale + this._mainCont.current.scrollTop).toString();
annoBox.style.left = (((rect.left - boundingRect.left) * scaleX) / pdfScale).toString();
- annoBox.style.width = ((rect.width * this._mainCont.current.offsetWidth) / boundingRect.width / pdfScale).toString();
- annoBox.style.height = ((rect.height * this._mainCont.current.offsetHeight) / boundingRect.height / pdfScale).toString();
+ annoBox.style.top = (((rect.top - boundingRect.top) * scaleY) / pdfScale + this._mainCont.current.scrollTop).toString();
+ annoBox.style.width = ((rect.width * scaleX) / pdfScale).toString();
+ annoBox.style.height = ((rect.height * scaleY) / pdfScale).toString();
this._annotationLayer.current && MarqueeAnnotator.previewNewAnnotation(this._savedAnnotations, this._annotationLayer.current, annoBox, this.getPageFromScroll(rect.top));
}
}
+ this._mainCont.current!.style.transform = '';
}
this._selectionContent = selRange.cloneContents();
this._selectionText = this._selectionContent?.textContent || '';
@@ -487,30 +480,19 @@ export class PDFViewer extends React.Component<IViewerProps> {
return (
<div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this.props.Document), transform: `scale(${NumCast(this.props.layoutDoc._freeform_scale, 1)})` }} ref={this._annotationLayer}>
{inlineAnnos.map(anno => (
- <Annotation {...this.props} fieldKey={this.props.fieldKey + '_annotations'} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
+ <Annotation {...this.props} fieldKey={this.props.fieldKey + '_annotations'} pointerEvents={this.pointerEvents} dataDoc={this.props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
))}
</div>
);
}
- @computed get overlayInfo() {
- return !this._overlayAnnoInfo ? null : (
- <div className="pdfViewerDash-overlayAnno" style={{ top: NumCast(this._overlayAnnoInfo.y), left: NumCast(this._overlayAnnoInfo.x) }}>
- <div className="pdfViewerDash-overlayAnno" style={{ right: -50, background: SharingManager.Instance.users.find(users => users.user.email === this._overlayAnnoInfo!.author)?.userColor }}>
- {this._overlayAnnoInfo.author + ' ' + Field.toString(this._overlayAnnoInfo.author_date as Field)}
- </div>
- </div>
- );
- }
-
getScrollHeight = () => this._scrollHeight;
- showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
scrollXf = () => (this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.layoutDoc._layout_scrollTop)) : this.props.ScreenToLocalTransform());
overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._freeform_scale, 1));
panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1);
panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
- transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
- opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(SnappingManager.GetCanEmbed() && this.props.isContentActive() ? [] : [Utils.IsOpaqueFilter()])];
+ transparentFilter = () => [...this.props.childFilters(), Utils.TransparentBackgroundFilter];
+ opaqueFilter = () => [...this.props.childFilters(), Utils.noDragDocsFilter, ...(SnappingManager.GetCanEmbed() && this.props.isContentActive() ? [] : [Utils.OpaqueBackgroundFilter])];
childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
if (doc instanceof Doc && property === StyleProp.PointerEvents) {
if (this.inlineTextAnnotations.includes(doc) || this.props.isContentActive() === false) return 'none';
@@ -572,6 +554,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
return <div className={'pdfViewerDash-text' + (this.props.pointerEvents?.() !== 'none' && this._textSelecting && this.props.isContentActive() ? '-selected' : '')} ref={this._viewer} />;
}
savedAnnotations = () => this._savedAnnotations;
+ addDocumentWrapper = (doc: Doc | Doc[]) => this.props.addDocument!(doc);
render() {
TraceMobx();
return (
@@ -590,17 +573,17 @@ export class PDFViewer extends React.Component<IViewerProps> {
{this.pdfViewerDiv}
{this.annotationLayer}
{this.overlayLayer}
- {this.overlayInfo}
{this._showWaiting ? <img className="pdfViewerDash-waiting" src={'/assets/loading.gif'} /> : null}
- {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+ {!this._mainCont.current || !this._annotationLayer.current ? null : (
<MarqueeAnnotator
+ ref={this._marqueeref}
rootDoc={this.props.rootDoc}
getPageFromScroll={this.getPageFromScroll}
anchorMenuClick={this.props.anchorMenuClick}
scrollTop={0}
- down={this._marqueeing}
- addDocument={(doc: Doc | Doc[]) => this.props.addDocument!(doc)}
- docView={this.props.docViewPath().lastElement()}
+ annotationLayerScrollTop={NumCast(this.props.Document._layout_scrollTop)}
+ addDocument={this.addDocumentWrapper}
+ docView={this.props.DocumentView!}
finishMarquee={this.finishMarquee}
savedAnnotations={this.savedAnnotations}
selectionText={this.selectionText}