aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/views/MarqueeAnnotator.tsx27
-rw-r--r--src/client/views/StyleProvider.tsx2
-rw-r--r--src/client/views/nodes/PDFBox.tsx4
-rw-r--r--src/client/views/nodes/WebBox.tsx2
-rw-r--r--src/client/views/nodes/WebBoxRenderer.js133
-rw-r--r--src/client/views/pdf/Annotation.tsx157
-rw-r--r--src/client/views/pdf/PDFViewer.tsx6
7 files changed, 157 insertions, 174 deletions
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index c4c00e0c3..ea73a53a9 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -100,22 +100,19 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
let maxX = -Number.MAX_VALUE;
let minY = Number.MAX_VALUE;
let maxY = -Number.MIN_VALUE;
- const annoDocs: Doc[] = [];
+ const annoRects: string[] = [];
savedAnnoMap.forEach((value: HTMLDivElement[]) =>
value.forEach(anno => {
- const textRegion = new Doc();
- textRegion.x = parseInt(anno.style.left ?? '0');
- textRegion.y = parseInt(anno.style.top ?? '0');
- textRegion._height = parseInt(anno.style.height ?? '0');
- textRegion._width = parseInt(anno.style.width ?? '0');
- textRegion.embedContainer = textRegionAnnoProto;
- textRegion.backgroundColor = color;
- annoDocs.push(textRegion);
+ const x = parseInt(anno.style.left ?? '0');
+ const y = parseInt(anno.style.top ?? '0');
+ const height = parseInt(anno.style.height ?? '0');
+ const width = parseInt(anno.style.width ?? '0');
+ annoRects.push(`${x}:${y}:${width}:${height}`);
anno.remove();
- minY = Math.min(NumCast(textRegion.y), minY);
- minX = Math.min(NumCast(textRegion.x), minX);
- maxY = Math.max(NumCast(textRegion.y) + NumCast(textRegion._height), maxY);
- maxX = Math.max(NumCast(textRegion.x) + NumCast(textRegion._width), maxX);
+ minY = Math.min(NumCast(y), minY);
+ minX = Math.min(NumCast(x), minX);
+ maxY = Math.max(NumCast(y) + NumCast(height), maxY);
+ maxX = Math.max(NumCast(x) + NumCast(width), maxX);
})
);
@@ -123,8 +120,10 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
textRegionAnnoProto.x = Math.max(minX, 0);
textRegionAnnoProto.height = Math.max(maxY, 0) - Math.max(minY, 0);
textRegionAnnoProto.width = Math.max(maxX, 0) - Math.max(minX, 0);
+ textRegionAnnoProto.backgroundColor = color;
// mainAnnoDocProto.text = this._selectionText;
- textRegionAnnoProto.text_inlineAnnotations = new List<Doc>(annoDocs);
+ textRegionAnnoProto.text_inlineAnnotations = new List<string>(annoRects);
+ textRegionAnnoProto.opacity = 0;
textRegionAnnoProto.layout_unrendered = true;
savedAnnoMap.clear();
return textRegionAnno;
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 3697aa010..6d4f69e6e 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -177,7 +177,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
return undefined;
case StyleProp.DocContents: return undefined;
case StyleProp.WidgetColor: return isAnnotated ? Colors.LIGHT_BLUE : 'dimgrey';
- case StyleProp.Opacity: return LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : doc?.text_inlineAnnotations ? 0 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null));
+ case StyleProp.Opacity: return LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null));
case StyleProp.FontColor: return StrCast(doc?.[fieldKey + 'fontColor'], StrCast(Doc.UserDoc().fontColor, color()));
case StyleProp.FontSize: return StrCast(doc?.[fieldKey + 'fontSize'], StrCast(Doc.UserDoc().fontSize));
case StyleProp.FontFamily: return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(Doc.UserDoc().fontFamily));
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index b03b90418..fdba9ff49 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -110,6 +110,10 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
crop = (region: Doc | undefined, addCrop?: boolean) => {
if (!region) return undefined;
const cropping = Doc.MakeCopy(region, true);
+ cropping.layout_unrendered = false; // text selection have this
+ cropping.text_inlineAnnotations = undefined; // text selections have this -- it causes them not to be rendered.
+ cropping.backgroundColor = undefined; // text selections have this -- it causes images to be fully transparent
+ cropping.opacity = undefined; // text selections have this -- it causes images to be fully transparent
const regionData = region[DocData];
regionData.lockedPosition = true;
regionData.title = 'region:' + this.Document.title;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index fc2e4bf61..c0fffd66c 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1030,7 +1030,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
.sort((a, b) => NumCast(a.y) - NumCast(b.y))
.map(anno => (
// eslint-disable-next-line react/jsx-props-no-spreading
- <Annotation {...this._props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
+ <Annotation {...this._props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} containerDataDoc={this.dataDoc} annoDoc={anno} key={`${anno[Id]}-annotation`} />
))}
</div>
);
diff --git a/src/client/views/nodes/WebBoxRenderer.js b/src/client/views/nodes/WebBoxRenderer.js
index 914adb404..6fb8f4957 100644
--- a/src/client/views/nodes/WebBoxRenderer.js
+++ b/src/client/views/nodes/WebBoxRenderer.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-undef */
/**
*
* @param {StyleSheetList} styleSheets
@@ -9,15 +10,14 @@ const ForeignHtmlRenderer = function (styleSheets) {
*
* @param {String} binStr
*/
- const binaryStringToBase64 = function (binStr) {
- return new Promise(resolve => {
+ const binaryStringToBase64 = binStr =>
+ new Promise(resolve => {
const reader = new FileReader();
reader.readAsDataURL(binStr);
reader.onloadend = function () {
resolve(reader.result);
};
});
- };
function prepend(extension) {
return window.location.origin + extension;
@@ -30,8 +30,8 @@ const ForeignHtmlRenderer = function (styleSheets) {
* @param {String} url
* @returns {Promise}
*/
- const getResourceAsBase64 = function (webUrl, inurl) {
- return new Promise((resolve, reject) => {
+ const getResourceAsBase64 = (webUrl, inurl) =>
+ new Promise(resolve => {
const xhr = new XMLHttpRequest();
// const url = inurl.startsWith("/") && !inurl.startsWith("//") ? webUrl + inurl : inurl;
// const url = CorsProxy(inurl.startsWith("/") && !inurl.startsWith("//") ? webUrl + inurl : inurl);// inurl.startsWith("http") ? CorsProxy(inurl) : inurl;
@@ -67,16 +67,15 @@ const ForeignHtmlRenderer = function (styleSheets) {
xhr.send(null);
});
- };
/**
*
* @param {String[]} urls
* @returns {Promise}
*/
- const getMultipleResourcesAsBase64 = function (webUrl, urls) {
+ const getMultipleResourcesAsBase64 = (webUrl, urls) => {
const promises = [];
- for (let i = 0; i < urls.length; i += 1) {
+ for (let i = 0; webUrl && i < urls.length; i += 1) {
promises.push(getResourceAsBase64(webUrl, urls[i]));
}
return Promise.all(promises);
@@ -130,6 +129,7 @@ const ForeignHtmlRenderer = function (styleSheets) {
const urlsFound = [];
let searchStartIndex = 0;
+ // eslint-disable-next-line no-constant-condition
while (true) {
const url = parseValue(cssRuleStr, searchStartIndex, selector, delimiters);
if (url === null) {
@@ -155,9 +155,6 @@ const ForeignHtmlRenderer = function (styleSheets) {
const getImageUrlsFromFromHtml = function (html) {
return getUrlsFromCssString(html, 'src=', [' ', '>', '\t'], true);
};
- const getSourceUrlsFromFromHtml = function (html) {
- return getUrlsFromCssString(html, 'source=', [' ', '>', '\t'], true);
- };
const escapeRegExp = function (string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
@@ -171,46 +168,45 @@ const ForeignHtmlRenderer = function (styleSheets) {
*
* @returns {Promise<String>}
*/
- const buildSvgDataUri = async function (webUrl, inputContentHtml, width, height, scroll, xoff) {
- return new Promise(async (resolve, reject) => {
- /* !! The problems !!
- * 1. CORS (not really an issue, expect perhaps for images, as this is a general security consideration to begin with)
- * 2. Platform won't wait for external assets to load (fonts, images, etc.)
- */
-
- // copy styles
- let cssStyles = '';
- const urlsFoundInCss = [];
-
- for (let i = 0; i < styleSheets.length; i += 1) {
- try {
- const rules = styleSheets[i].cssRules;
- for (let j = 0; j < rules.length; j += 1) {
- const cssRuleStr = rules[j].cssText;
- urlsFoundInCss.push(...getUrlsFromCssString(cssRuleStr));
- cssStyles += cssRuleStr;
- }
- } catch (e) {
- /* empty */
+ const buildSvgDataUri = (webUrl, inputContentHtml, width, height, scroll, xoff) => {
+ /* !! The problems !!
+ * 1. CORS (not really an issue, expect perhaps for images, as this is a general security consideration to begin with)
+ * 2. Platform won't wait for external assets to load (fonts, images, etc.)
+ */
+
+ // copy styles
+ let cssStyles = '';
+ const urlsFoundInCss = [];
+
+ for (let i = 0; i < styleSheets.length; i += 1) {
+ try {
+ const rules = styleSheets[i].cssRules;
+ for (let j = 0; j < rules.length; j += 1) {
+ const cssRuleStr = rules[j].cssText;
+ urlsFoundInCss.push(...getUrlsFromCssString(cssRuleStr));
+ cssStyles += cssRuleStr;
}
+ } catch (e) {
+ /* empty */
}
+ }
- // const fetchedResourcesFromStylesheets = await getMultipleResourcesAsBase64(webUrl, urlsFoundInCss);
- // for (let i = 0; i < fetchedResourcesFromStylesheets.length; i++) {
- // const r = fetchedResourcesFromStylesheets[i];
- // if (r.resourceUrl) {
- // cssStyles = cssStyles.replace(new RegExp(escapeRegExp(r.resourceUrl), "g"), r.resourceBase64);
- // }
- // }
-
- let contentHtml = inputContentHtml
- .replace(/<source[^>]*>/g, '') // <picture> tags have a <source> which has a srcset field of image refs. instead of converting each, just use the default <img> of the picture
- .replace(/noscript/g, 'div')
- .replace(/<div class="mediaset"><\/div>/g, '') // when scripting isn't available (ie, rendering web pages here), <noscript> tags should become <div>'s. But for Brown CS, there's a layout problem if you leave the empty <mediaset> tag
- .replace(/<link[^>]*>/g, '') // don't need to keep any linked style sheets because we've already processed all style sheets above
- .replace(/srcset="([^ "]*)[^"]*"/g, 'src="$1"'); // instead of converting each item in the srcset to a data url, just convert the first one and use that
- const urlsFoundInHtml = getImageUrlsFromFromHtml(contentHtml).filter(url => !url.startsWith('data:'));
- const fetchedResources = webUrl ? await getMultipleResourcesAsBase64(webUrl, urlsFoundInHtml) : [];
+ // const fetchedResourcesFromStylesheets = await getMultipleResourcesAsBase64(webUrl, urlsFoundInCss);
+ // for (let i = 0; i < fetchedResourcesFromStylesheets.length; i++) {
+ // const r = fetchedResourcesFromStylesheets[i];
+ // if (r.resourceUrl) {
+ // cssStyles = cssStyles.replace(new RegExp(escapeRegExp(r.resourceUrl), "g"), r.resourceBase64);
+ // }
+ // }
+
+ let contentHtml = inputContentHtml
+ .replace(/<source[^>]*>/g, '') // <picture> tags have a <source> which has a srcset field of image refs. instead of converting each, just use the default <img> of the picture
+ .replace(/noscript/g, 'div')
+ .replace(/<div class="mediaset"><\/div>/g, '') // when scripting isn't available (ie, rendering web pages here), <noscript> tags should become <div>'s. But for Brown CS, there's a layout problem if you leave the empty <mediaset> tag
+ .replace(/<link[^>]*>/g, '') // don't need to keep any linked style sheets because we've already processed all style sheets above
+ .replace(/srcset="([^ "]*)[^"]*"/g, 'src="$1"'); // instead of converting each item in the srcset to a data url, just convert the first one and use that
+ const urlsFoundInHtml = getImageUrlsFromFromHtml(contentHtml).filter(url => !url.startsWith('data:'));
+ return getMultipleResourcesAsBase64(webUrl, urlsFoundInHtml).then(fetchedResources => {
for (let i = 0; i < fetchedResources.length; i += 1) {
const r = fetchedResources[i];
if (r.resourceUrl) {
@@ -243,9 +239,7 @@ const ForeignHtmlRenderer = function (styleSheets) {
</svg>`;
// convert SVG to data-uri
- const dataUri = `data:image/svg+xml;base64,${window.btoa(unescape(encodeURIComponent(svg)))}`;
-
- resolve(dataUri);
+ return `data:image/svg+xml;base64,${window.btoa(unescape(encodeURIComponent(svg)))}`;
});
};
@@ -256,18 +250,19 @@ const ForeignHtmlRenderer = function (styleSheets) {
*
* @return {Promise<Image>}
*/
- this.renderToImage = async function (webUrl, html, width, height, scroll, xoff) {
- return new Promise(async (resolve, reject) => {
+ this.renderToImage = (webUrl, html, width, height, scroll, xoff) =>
+ new Promise(resolve => {
const img = new Image();
- console.log(`BUILDING SVG for: ${webUrl}`);
- img.src = await buildSvgDataUri(webUrl, html, width, height, scroll, xoff);
-
img.onload = function () {
console.log(`IMAGE SVG created: ${webUrl}`);
resolve(img);
};
+ console.log(`BUILDING SVG for: ${webUrl}`);
+ buildSvgDataUri(webUrl, html, width, height, scroll, xoff).then(uri => {
+ img.src = uri;
+ return img;
+ });
});
- };
/**
* @param {String} html
@@ -276,20 +271,16 @@ const ForeignHtmlRenderer = function (styleSheets) {
*
* @return {Promise<Image>}
*/
- this.renderToCanvas = async function (webUrl, html, width, height, scroll, xoff, oversample) {
- return new Promise(async (resolve, reject) => {
- const img = await self.renderToImage(webUrl, html, width, height, scroll, xoff);
-
+ this.renderToCanvas = (webUrl, html, width, height, scroll, xoff, oversample) =>
+ self.renderToImage(webUrl, html, width, height, scroll, xoff).then(img => {
const canvas = document.createElement('canvas');
canvas.width = img.width * oversample;
canvas.height = img.height * oversample;
const canvasCtx = canvas.getContext('2d');
canvasCtx.drawImage(img, 0, 0, img.width * oversample, img.height * oversample);
-
- resolve(canvas);
+ return canvas;
});
- };
/**
* @param {String} html
@@ -298,12 +289,10 @@ const ForeignHtmlRenderer = function (styleSheets) {
*
* @return {Promise<String>}
*/
- this.renderToBase64Png = async function (webUrl, html, width, height, scroll, xoff, oversample) {
- return new Promise(async (resolve, reject) => {
- const canvas = await self.renderToCanvas(webUrl, html, width, height, scroll, xoff, oversample);
- resolve(canvas.toDataURL('image/png'));
- });
- };
+ this.renderToBase64Png = (webUrl, html, width, height, scroll, xoff, oversample) =>
+ self
+ .renderToCanvas(webUrl, html, width, height, scroll, xoff, oversample) //
+ .then(canvas => canvas.toDataURL('image/png'));
};
export function CreateImage(webUrl, styleSheets, html, width, height, scroll, xoff = 0, oversample = 1) {
@@ -379,11 +368,11 @@ const ClipboardUtils = new (function () {
.then(result => {
loadFile(result, callback);
})
- .catch(error => {
+ .catch(() => {
callback(null, 'Reading clipboard error.');
});
})
- .catch(error => {
+ .catch(() => {
callback(null, 'Reading clipboard error.');
});
} else {
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 38578837a..f1cd1a4f7 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -1,9 +1,8 @@
-/* eslint-disable react/jsx-props-no-spreading */
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, DocCast, NumCast, StrCast } from '../../../fields/Types';
import { LinkFollower } from '../../util/LinkFollower';
@@ -14,13 +13,38 @@ import { OpenWhere } from '../nodes/DocumentView';
import { FieldViewProps } from '../nodes/FieldView';
import { AnchorMenu } from './AnchorMenu';
import './Annotation.scss';
-import { Highlight } from '../../../fields/DocSymbols';
+
+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
@@ -31,64 +55,25 @@ export class Annotation extends ObservableReactComponent<IAnnotationProps> {
}
@computed get linkHighlighted() {
- const found = LinkManager.Instance.getAllDirectLinks(this._props.anno).find(link => {
- const a1 = LinkManager.getOppositeAnchor(link, this._props.anno);
+ const found = LinkManager.Instance.getAllDirectLinks(this._props.annoDoc).find(link => {
+ const a1 = LinkManager.getOppositeAnchor(link, this._props.annoDoc);
return a1 && Doc.GetBrushStatus(DocCast(a1.annotationOn, a1));
});
return found;
}
- linkHighlightedFunc = () => this.linkHighlighted;
- highlightedFunc = () => this._props.anno[Highlight];
deleteAnnotation = undoable(() => {
- const docAnnotations = DocListCast(this._props.dataDoc[this._props.fieldKey]);
- this._props.dataDoc[this._props.fieldKey] = new List<Doc>(docAnnotations.filter(a => a !== this._props.anno));
+ 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.anno, {}), 'pin to pres');
-
- render() {
- return (
- <div style={{ display: this._props.anno.textCopied && !Doc.GetBrushHighlightStatus(this._props.anno) ? 'none' : undefined }}>
- {DocListCast(this._props.anno.text_inlineAnnotations).map(a => (
- // eslint-disable-next-line no-use-before-define
- <RegionAnnotation
- pointerEvents={this._props.pointerEvents}
- {...this._props}
- highlighted={this.highlightedFunc}
- linkHighlighted={this.linkHighlightedFunc}
- pinToPres={this.pinToPres}
- deleteAnnotation={this.deleteAnnotation}
- document={a}
- key={a[Id]}
- />
- ))}
- </div>
- );
- }
-}
-
-interface IRegionAnnotationProps extends IAnnotationProps {
- document: Doc;
- linkHighlighted: () => Doc | undefined;
- highlighted: () => any;
- deleteAnnotation: () => void;
- pinToPres: (...args: any[]) => void;
- pointerEvents?: () => Opt<string>;
-}
-@observer
-class RegionAnnotation extends ObservableReactComponent<IRegionAnnotationProps> {
- private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
-
- @computed get regionDoc() {
- return DocCast(this._props.document.embedContainer, this._props.document);
- }
+ pinToPres = undoable(() => this._props.pinToPres(this._props.annoDoc, {}), 'pin to pres');
- makeTargetToggle = undoable(() => { this.regionDoc.followLinkToggle = !this.regionDoc.followLinkToggle }, "set link toggle"); // prettier-ignore
+ makeTargetToggle = undoable(() => { this._props.annoDoc.followLinkToggle = !this._props.annoDoc.followLinkToggle }, "set link toggle"); // prettier-ignore
- isTargetToggler = () => BoolCast(this.regionDoc.followLinkToggle);
+ isTargetToggler = () => BoolCast(this._props.annoDoc.followLinkToggle);
showTargetTrail = undoable((anchor: Doc) => {
const trail = DocCast(anchor.presentationTrail);
@@ -101,12 +86,12 @@ class RegionAnnotation extends ObservableReactComponent<IRegionAnnotationProps>
@action
onContextMenu = (e: React.MouseEvent) => {
AnchorMenu.Instance.Status = 'annotation';
- AnchorMenu.Instance.Delete = this._props.deleteAnnotation;
+ AnchorMenu.Instance.Delete = this.deleteAnnotation;
AnchorMenu.Instance.Pinned = false;
- AnchorMenu.Instance.PinToPres = this._props.pinToPres;
+ AnchorMenu.Instance.PinToPres = this.pinToPres;
AnchorMenu.Instance.MakeTargetToggle = this.makeTargetToggle;
AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler;
- AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this.regionDoc);
+ AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this._props.annoDoc);
AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
e.stopPropagation();
e.preventDefault();
@@ -118,37 +103,43 @@ class RegionAnnotation extends ObservableReactComponent<IRegionAnnotationProps>
e.preventDefault();
} else if (e.button === 0) {
e.stopPropagation();
- LinkFollower.FollowLink(undefined, this.regionDoc, false);
+ LinkFollower.FollowLink(undefined, this._props.annoDoc, false);
}
};
-
+ 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.regionDoc && Doc.GetBrushHighlightStatus(this.regionDoc);
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: this._props.linkHighlighted() ? 'solid 1px lightBlue' : undefined,
- backgroundColor: this._props.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>
);
}
}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index a3fd192f7..22355bc57 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -388,7 +388,6 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
if (!e.altKey && e.button === 0 && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
this._props.select(false);
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- 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'))) {
@@ -400,6 +399,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, 'htmlAnnotation', { 'pointer-events': 'none' });
document.addEventListener('pointerup', this.onSelectEnd);
}
+ this._marqueeref.current?.onInitiateSelection([e.clientX, e.clientY]);
}
};
@@ -505,7 +505,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
<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 => (
// eslint-disable-next-line react/jsx-props-no-spreading
- <Annotation {...this._props} fieldKey={this._props.fieldKey + '_annotations'} pointerEvents={this.pointerEvents} dataDoc={this._props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
+ <Annotation {...this._props} fieldKey={this._props.fieldKey + '_annotations'} pointerEvents={this.pointerEvents} containerDataDoc={this._props.dataDoc} annoDoc={anno} key={`${anno[Id]}-annotation`} />
))}
</div>
);
@@ -615,7 +615,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
selectionText={this.selectionText}
annotationLayer={this._annotationLayer.current}
marqueeContainer={this._mainCont.current}
- anchorMenuCrop={this._textSelecting ? undefined : this.crop}
+ anchorMenuCrop={this.crop}
/>
)}
</div>