aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/goldenLayout.js11
-rw-r--r--src/client/util/DragManager.ts2
-rw-r--r--src/client/util/HypothesisUtils.ts160
-rw-r--r--src/client/views/DocumentDecorations.scss5
-rw-r--r--src/client/views/DocumentDecorations.tsx9
-rw-r--r--src/client/views/GlobalKeyHandler.ts2
-rw-r--r--src/client/views/Main.tsx7
-rw-r--r--src/client/views/MainView.scss9
-rw-r--r--src/client/views/MainView.tsx94
-rw-r--r--src/client/views/MarqueeAnnotator.tsx6
-rw-r--r--src/client/views/SidebarAnnos.tsx22
-rw-r--r--src/client/views/StyleProvider.tsx6
-rw-r--r--src/client/views/collections/CollectionDockingView.scss4
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx17
-rw-r--r--src/client/views/collections/TabDocView.tsx6
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx3
-rw-r--r--src/client/views/nodes/PDFBox.tsx13
-rw-r--r--src/client/views/nodes/WebBox.tsx14
-rw-r--r--src/client/views/nodes/button/FontIconBox.tsx12
-rw-r--r--src/client/views/nodes/formattedText/DashDocCommentView.tsx8
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx12
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx53
-rw-r--r--src/client/views/nodes/formattedText/EquationView.tsx10
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx1
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts7
-rw-r--r--src/client/views/nodes/formattedText/SummaryView.tsx9
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts1
-rw-r--r--src/client/views/pdf/PDFViewer.scss12
-rw-r--r--src/client/views/pdf/PDFViewer.tsx6
-rw-r--r--src/debug/Repl.tsx37
30 files changed, 278 insertions, 280 deletions
diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js
index dd11e6466..bc08b4d0b 100644
--- a/src/client/goldenLayout.js
+++ b/src/client/goldenLayout.js
@@ -459,7 +459,7 @@
this._bDragging = false;
this.emit('dragStop', oEvent, this._nOriginalX + this._nX);
} else { // make title receive pointer events to allow setting insertion position or selecting texst range
- const classname = typeof oEvent.target?.className === "string" ? oEvent.target.className : "";
+ const classname = oEvent.target ? (typeof oEvent.target.className === "string" ? oEvent.target.className : ""): "";
if (classname.includes("lm_title_wrap")) {
oEvent.target.children[0].style.pointerEvents = "all";
oEvent.target.children[0].focus();
@@ -5391,10 +5391,11 @@
* @returns {void}
*/
_render: function () {
- this._reactComponent = ReactDOM.render(this._getReactComponent(), this._container.getElement()[0]);
- this._originalComponentWillUpdate = this._reactComponent.componentWillUpdate || function () {
+ this._reactComponent = ReactDOM.createRoot(this._container.getElement()[0])
+ this._reactComponent.render(this._getReactComponent());
+ this._originalComponentWillUpdate = this._reactComponent.componentDidUpdate || function () {
};
- this._reactComponent.componentWillUpdate = this._onUpdate.bind(this);
+ this._reactComponent.componentDidUpdate = this._onUpdate.bind(this);
if (this._container.getState()) {
this._reactComponent.setState(this._container.getState());
}
@@ -5407,7 +5408,7 @@
* @returns {void}
*/
_destroy: function () {
- ReactDOM.unmountComponentAtNode(this._container.getElement()[0]);
+ // ReactDOM.unmountComponentAtNode(this._container.getElement()[0]);
this._container.off('open', this._render, this);
this._container.off('destroy', this._destroy, this);
},
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 36e5a65fb..d0690fa10 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -450,7 +450,7 @@ export namespace DragManager {
const hideDragShowOriginalElements = (hide: boolean) => {
dragLabel.style.display = hide ? '' : 'none';
!hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
- eles.forEach(ele => (ele.hidden = hide));
+ setTimeout(() => eles.forEach(ele => (ele.hidden = hide)));
};
options?.hideSource && hideDragShowOriginalElements(true);
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index e910a9118..297520d5d 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -1,30 +1,28 @@
-import { StrCast, Cast } from "../../fields/Types";
-import { SearchUtil } from "./SearchUtil";
-import { action, runInAction } from "mobx";
-import { Doc, Opt } from "../../fields/Doc";
-import { DocumentType } from "../documents/DocumentTypes";
-import { Docs } from "../documents/Documents";
-import { SelectionManager } from "./SelectionManager";
-import { WebField } from "../../fields/URLField";
-import { DocumentManager } from "./DocumentManager";
-import { DocumentLinksButton } from "../views/nodes/DocumentLinksButton";
-import { simulateMouseClick, Utils } from "../../Utils";
-import { DocumentView } from "../views/nodes/DocumentView";
-import { Id } from "../../fields/FieldSymbols";
+import { StrCast, Cast } from '../../fields/Types';
+import { SearchUtil } from './SearchUtil';
+import { action, runInAction } from 'mobx';
+import { Doc, Opt } from '../../fields/Doc';
+import { DocumentType } from '../documents/DocumentTypes';
+import { Docs } from '../documents/Documents';
+import { SelectionManager } from './SelectionManager';
+import { WebField } from '../../fields/URLField';
+import { DocumentManager } from './DocumentManager';
+import { DocumentLinksButton } from '../views/nodes/DocumentLinksButton';
+import { simulateMouseClick, Utils } from '../../Utils';
+import { DocumentView } from '../views/nodes/DocumentView';
+import { Id } from '../../fields/FieldSymbols';
export namespace Hypothesis {
-
/**
- * Retrieve a WebDocument with the given url, prioritizing results that are on screen.
+ * Retrieve a WebDocument with the given url, prioritizing results that are on screen.
* If none exist, create and return a new WebDocument.
*/
export const getSourceWebDoc = async (uri: string) => {
const result = await findWebDoc(uri);
- console.log(result ? "existing doc found" : "existing doc NOT found");
+ console.log(result ? 'existing doc found' : 'existing doc NOT found');
return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _height: 512, _width: 400, useCors: true }); // create and return a new Web doc with given uri if no matching docs are found
};
-
/**
* Search for a WebDocument whose url field matches the given uri, return undefined if not found
*/
@@ -33,18 +31,18 @@ export namespace Hypothesis {
if (currentDoc && Cast(currentDoc.data, WebField)?.url.href === uri) return currentDoc; // always check first whether the currently selected doc is the annotation's source, only use Search otherwise
const results: Doc[] = [];
- await SearchUtil.Search("web", true).then(action(async (res: SearchUtil.DocSearchResult) => {
- const docs = res.docs;
- const filteredDocs = docs.filter(doc =>
- doc.author === Doc.CurrentUserEmail && doc.type === DocumentType.WEB && doc.data
- );
- filteredDocs.forEach(doc => {
- uri === Cast(doc.data, WebField)?.url.href && results.push(doc); // TODO check visited sites history?
- });
- }));
+ await SearchUtil.Search('web', true).then(
+ action(async (res: SearchUtil.DocSearchResult) => {
+ const docs = res.docs;
+ const filteredDocs = docs.filter(doc => doc.author === Doc.CurrentUserEmail && doc.type === DocumentType.WEB && doc.data);
+ filteredDocs.forEach(doc => {
+ uri === Cast(doc.data, WebField)?.url.href && results.push(doc); // TODO check visited sites history?
+ });
+ })
+ );
const onScreenResults = results.filter(doc => DocumentManager.Instance.getFirstDocumentView(doc));
- return onScreenResults.length ? onScreenResults[0] : (results.length ? results[0] : undefined); // prioritize results that are currently on the screen
+ return onScreenResults.length ? onScreenResults[0] : results.length ? results[0] : undefined; // prioritize results that are currently on the screen
};
/**
@@ -52,17 +50,19 @@ export namespace Hypothesis {
*/
export const linkListener = async (e: any) => {
const annotationId: string = e.detail.id;
- const annotationUri: string = StrCast(e.detail.uri).split("#annotations:")[0]; // clean hypothes.is URLs that reference a specific annotation
+ const annotationUri: string = StrCast(e.detail.uri).split('#annotations:')[0]; // clean hypothes.is URLs that reference a specific annotation
const sourceDoc: Doc = await getSourceWebDoc(annotationUri);
- if (!DocumentLinksButton.StartLink || sourceDoc === DocumentLinksButton.StartLink) { // start new link if there were none already started, or if the old startLink came from the same web document (prevent links to itself)
+ if (!DocumentLinksButton.StartLink || sourceDoc === DocumentLinksButton.StartLink) {
+ // start new link if there were none already started, or if the old startLink came from the same web document (prevent links to itself)
runInAction(() => {
DocumentLinksButton.AnnotationId = annotationId;
DocumentLinksButton.AnnotationUri = annotationUri;
DocumentLinksButton.StartLink = sourceDoc;
DocumentLinksButton.StartLinkView = undefined;
});
- } else { // if a link has already been started, complete the link to sourceDoc
+ } else {
+ // if a link has already been started, complete the link to sourceDoc
runInAction(() => {
DocumentLinksButton.AnnotationId = annotationId;
DocumentLinksButton.AnnotationUri = annotationUri;
@@ -81,31 +81,41 @@ export namespace Hypothesis {
export const makeLink = async (title: string, url: string, annotationId: string, annotationSourceDoc: Doc) => {
// if the annotation's source webpage isn't currently loaded in Dash, we're not able to access and edit the annotation from the client
// so we're loading the webpage and its annotations invisibly in a WebBox in MainView.tsx, until the editing is done
- !DocumentManager.Instance.getFirstDocumentView(annotationSourceDoc) && runInAction(() => DocumentLinksButton.invisibleWebDoc = annotationSourceDoc);
+ //!DocumentManager.Instance.getFirstDocumentView(annotationSourceDoc) && runInAction(() => DocumentLinksButton.invisibleWebDoc = annotationSourceDoc);
var success = false;
const onSuccess = action(() => {
- console.log("Edit success!!");
+ console.log('Edit success!!');
success = true;
clearTimeout(interval);
- DocumentLinksButton.invisibleWebDoc = undefined;
- document.removeEventListener("editSuccess", onSuccess);
+ //DocumentLinksButton.invisibleWebDoc = undefined;
+ document.removeEventListener('editSuccess', onSuccess);
});
const newHyperlink = `[${title}\n](${url})`;
- const interval = setInterval(() => // keep trying to edit until annotations have loaded and editing is successful
- !success && document.dispatchEvent(new CustomEvent<{ newHyperlink: string, id: string }>("addLink", {
- detail: { newHyperlink: newHyperlink, id: annotationId },
- bubbles: true
- })), 300);
-
- setTimeout(action(() => {
- if (!success) {
- clearInterval(interval);
- DocumentLinksButton.invisibleWebDoc = undefined;
- }
- }), 10000); // give up if no success after 10s
- document.addEventListener("editSuccess", onSuccess);
+ const interval = setInterval(
+ () =>
+ // keep trying to edit until annotations have loaded and editing is successful
+ !success &&
+ document.dispatchEvent(
+ new CustomEvent<{ newHyperlink: string; id: string }>('addLink', {
+ detail: { newHyperlink: newHyperlink, id: annotationId },
+ bubbles: true,
+ })
+ ),
+ 300
+ );
+
+ setTimeout(
+ action(() => {
+ if (!success) {
+ clearInterval(interval);
+ //DocumentLinksButton.invisibleWebDoc = undefined;
+ }
+ }),
+ 10000
+ ); // give up if no success after 10s
+ document.addEventListener('editSuccess', onSuccess);
};
/**
@@ -114,33 +124,40 @@ export namespace Hypothesis {
export const deleteLink = async (linkDoc: Doc, sourceDoc: Doc, destinationDoc: Doc) => {
if (Cast(destinationDoc.data, WebField)?.url.href !== StrCast(linkDoc.annotationUri)) return; // check that the destinationDoc is a WebDocument containing the target annotation
- !DocumentManager.Instance.getFirstDocumentView(destinationDoc) && runInAction(() => DocumentLinksButton.invisibleWebDoc = destinationDoc); // see note in makeLink
+ //!DocumentManager.Instance.getFirstDocumentView(destinationDoc) && runInAction(() => DocumentLinksButton.invisibleWebDoc = destinationDoc); // see note in makeLink
var success = false;
const onSuccess = action(() => {
- console.log("Edit success!");
+ console.log('Edit success!');
success = true;
clearTimeout(interval);
- DocumentLinksButton.invisibleWebDoc = undefined;
- document.removeEventListener("editSuccess", onSuccess);
+ // DocumentLinksButton.invisibleWebDoc = undefined;
+ document.removeEventListener('editSuccess', onSuccess);
});
const annotationId = StrCast(linkDoc.annotationId);
const linkUrl = Doc.globalServerPath(sourceDoc);
- const interval = setInterval(() => {// keep trying to edit until annotations have loaded and editing is successful
- !success && document.dispatchEvent(new CustomEvent<{ targetUrl: string, id: string }>("deleteLink", {
- detail: { targetUrl: linkUrl, id: annotationId },
- bubbles: true
- }));
+ const interval = setInterval(() => {
+ // keep trying to edit until annotations have loaded and editing is successful
+ !success &&
+ document.dispatchEvent(
+ new CustomEvent<{ targetUrl: string; id: string }>('deleteLink', {
+ detail: { targetUrl: linkUrl, id: annotationId },
+ bubbles: true,
+ })
+ );
}, 300);
- setTimeout(action(() => {
- if (!success) {
- clearInterval(interval);
- DocumentLinksButton.invisibleWebDoc = undefined;
- }
- }), 10000); // give up if no success after 10s
- document.addEventListener("editSuccess", onSuccess);
+ setTimeout(
+ action(() => {
+ if (!success) {
+ clearInterval(interval);
+ //DocumentLinksButton.invisibleWebDoc = undefined;
+ }
+ }),
+ 10000
+ ); // give up if no success after 10s
+ document.addEventListener('editSuccess', onSuccess);
};
/**
@@ -149,17 +166,20 @@ export namespace Hypothesis {
export const scrollToAnnotation = (annotationId: string, target: Doc) => {
var success = false;
const onSuccess = () => {
- console.log("Scroll success!!");
+ console.log('Scroll success!!');
document.removeEventListener('scrollSuccess', onSuccess);
clearInterval(interval);
success = true;
};
- const interval = setInterval(() => { // keep trying to scroll every 250ms until annotations have loaded and scrolling is successful
- document.dispatchEvent(new CustomEvent('scrollToAnnotation', {
- detail: annotationId,
- bubbles: true
- }));
+ const interval = setInterval(() => {
+ // keep trying to scroll every 250ms until annotations have loaded and scrolling is successful
+ document.dispatchEvent(
+ new CustomEvent('scrollToAnnotation', {
+ detail: annotationId,
+ bubbles: true,
+ })
+ );
const targetView: Opt<DocumentView> = DocumentManager.Instance.getFirstDocumentView(target);
const position = targetView?.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
targetView && position && simulateMouseClick(targetView.ContentDiv!, position[0], position[1], position[0], position[1], false);
@@ -168,4 +188,4 @@ export namespace Hypothesis {
document.addEventListener('scrollSuccess', onSuccess); // listen for success message from client
setTimeout(() => !success && clearInterval(interval), 10000); // give up if no success after 10s
};
-} \ No newline at end of file
+}
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 4e0b061a6..fe1190e27 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -250,8 +250,8 @@ $resizeHandler: 8px;
.documentDecorations-lock {
position: absolute;
background: black;
- right: 11;
- top: 30px;
+ right: 23px;
+ top: 3px;
color: gray;
height: 14;
width: 14;
@@ -260,6 +260,7 @@ $resizeHandler: 8px;
display: flex;
align-items: center;
flex-direction: column;
+ border-radius: 15%;
}
.documentDecorations-rotationPath {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 06eb6c6d7..8e0686038 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -782,6 +782,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
bounds.r = Math.max(bounds.x, Math.max(leftBounds, Math.min(window.innerWidth, bounds.r + borderRadiusDraggerWidth + this._resizeBorderWidth / 2) - this._resizeBorderWidth / 2 - borderRadiusDraggerWidth));
bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight));
+ const useLock = bounds.r - bounds.x > 120;
const useRotation = seldocview.rootDoc.type !== DocumentType.EQUATION; // when do we want an object to not rotate?
const rotation = NumCast(seldocview.rootDoc._rotation);
@@ -856,9 +857,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
onContextMenu={e => e.preventDefault()}
/>
)}
- <div key="lock" className="documentDecorations-lock" style={{ color: seldocview.rootDoc._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown} onContextMenu={e => e.preventDefault()}>
- <FontAwesomeIcon size="sm" icon="lock" />
- </div>
+ {!useLock ? null : (
+ <div key="lock" className="documentDecorations-lock" style={{ color: seldocview.rootDoc._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown} onContextMenu={e => e.preventDefault()}>
+ <FontAwesomeIcon size="sm" icon="lock" />
+ </div>
+ )}
{hideDocumentButtonBar ? null : (
<div
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index c19e6831f..5a6caf995 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -139,7 +139,7 @@ export class KeyManager {
document.body.focus();
break;
case 'enter': {
- DocumentDecorations.Instance.onCloseClick(false);
+ //DocumentDecorations.Instance.onCloseClick(false);
break;
}
case 'delete':
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 3e6539c5e..f327f3184 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -3,9 +3,8 @@
// }
import * as React from 'react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
import { AssignAllExtensions } from '../../extensions/General/Extensions';
-import { Utils } from '../../Utils';
import { DocServer } from '../DocServer';
import { Docs } from '../documents/Documents';
import { CurrentUserUtils } from '../util/CurrentUserUtils';
@@ -14,7 +13,7 @@ import { ReplayMovements } from '../util/ReplayMovements';
import { TrackMovements } from '../util/TrackMovements';
import { CollectionView } from './collections/CollectionView';
import { MainView } from './MainView';
-import * as dotenv from 'dotenv' // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
+import * as dotenv from 'dotenv'; // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
dotenv.config();
AssignAllExtensions();
@@ -45,6 +44,6 @@ AssignAllExtensions();
new LinkManager();
new TrackMovements();
new ReplayMovements();
- ReactDOM.render(<MainView />, document.getElementById('root'));
+ ReactDOM.createRoot(document.getElementById('root')!).render(<MainView />);
}, 0);
})();
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index c5ac1cf52..069206126 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -371,12 +371,3 @@ h1,
width: 200px;
height: 800px;
}
-
-.mainVew-invisibleWebRef {
- position: absolute;
- left: 50;
- top: 50;
- display: block;
- width: 500px;
- height: 1000px;
-}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 052846e71..9648a7807 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -7,7 +7,7 @@ import { action, computed, configure, observable, reaction, runInAction } from '
import { observer } from 'mobx-react';
import 'normalize.css';
import * as React from 'react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
import { Doc, DocListCast, Opt } from '../../fields/Doc';
import { ScriptField } from '../../fields/ScriptField';
import { DocCast, StrCast } from '../../fields/Types';
@@ -948,40 +948,6 @@ export class MainView extends React.Component {
);
}
- @computed get invisibleWebBox() {
- // see note under the makeLink method in HypothesisUtils.ts
- return !DocumentLinksButton.invisibleWebDoc ? null : (
- <div className="mainView-invisibleWebRef" ref={DocumentLinksButton.invisibleWebRef}>
- <WebBox
- fieldKey={'data'}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined}
- Document={DocumentLinksButton.invisibleWebDoc}
- dropAction={'move'}
- styleProvider={undefined}
- isSelected={returnFalse}
- select={returnFalse}
- setHeight={returnFalse}
- rootSelected={returnFalse}
- renderDepth={0}
- addDocTab={returnFalse}
- pinToPres={returnFalse}
- ScreenToLocalTransform={Transform.Identity}
- bringToFront={returnFalse}
- isContentActive={emptyFunction}
- whenChildContentsActiveChanged={returnFalse}
- focus={returnFalse}
- docViewPath={returnEmptyDoclist}
- PanelWidth={() => 500}
- PanelHeight={() => 800}
- docFilters={returnEmptyFilter}
- docRangeFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- />
- </div>
- );
- }
-
render() {
return (
<div
@@ -1037,68 +1003,10 @@ export class MainView extends React.Component {
<RichTextMenu />
<InkTranscription />
{this.snapLines}
- <div className="mainView-webRef" ref={this.makeWebRef} />
<LightboxView key="lightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
</div>
);
}
-
- makeWebRef = (ele: HTMLDivElement) => {
- reaction(
- () => DocumentLinksButton.invisibleWebDoc,
- invisibleDoc => {
- ReactDOM.unmountComponentAtNode(ele);
- invisibleDoc &&
- ReactDOM.render(
- <span title="Drag as document" className="invisible-webbox">
- <div className="mainView-webRef" ref={DocumentLinksButton.invisibleWebRef}>
- <WebBox
- fieldKey={'data'}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined}
- Document={invisibleDoc}
- dropAction={'move'}
- isSelected={returnFalse}
- docViewPath={returnEmptyDoclist}
- select={returnFalse}
- rootSelected={returnFalse}
- renderDepth={0}
- setHeight={returnFalse}
- styleProvider={undefined}
- addDocTab={returnFalse}
- pinToPres={returnFalse}
- ScreenToLocalTransform={Transform.Identity}
- bringToFront={returnFalse}
- isContentActive={emptyFunction}
- whenChildContentsActiveChanged={returnFalse}
- focus={returnFalse}
- PanelWidth={() => 500}
- PanelHeight={() => 800}
- docFilters={returnEmptyFilter}
- docRangeFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- />
- </div>
- ;
- </span>,
- ele
- );
-
- let success = false;
- const onSuccess = () => {
- success = true;
- clearTimeout(interval);
- document.removeEventListener('editSuccess', onSuccess);
- };
-
- // For some reason, Hypothes.is annotations don't load until a click is registered on the page,
- // so we keep simulating clicks until annotations have loaded and editing is successful
- const interval = setInterval(() => !success && simulateMouseClick(ele, 50, 50, 50, 50), 500);
- setTimeout(() => !success && clearInterval(interval), 10000); // give up if no success after 10s
- document.addEventListener('editSuccess', onSuccess);
- }
- );
- };
}
ScriptingGlobals.add(function selectMainMenu(doc: Doc, title: string) {
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index d9a989309..ce0e58d90 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -27,6 +27,7 @@ export interface MarqueeAnnotatorProps {
mainCont: HTMLDivElement;
docView: DocumentView;
savedAnnotations: () => ObservableMap<number, HTMLDivElement[]>;
+ selectionText: () => string;
annotationLayer: HTMLDivElement;
addDocument: (doc: Doc) => boolean;
getPageFromScroll?: (top: number) => number;
@@ -147,7 +148,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
return marqueeAnno;
}
- const textRegionAnno = Docs.Create.HTMLAnchorDocument([], { annotationOn: this.props.rootDoc, backgroundColor: 'transparent', title: 'Selection on ' + this.props.rootDoc.title });
+ const textRegionAnno = Docs.Create.HTMLAnchorDocument([], { annotationOn: this.props.rootDoc, text: this.props.selectionText(), backgroundColor: 'transparent', title: 'Selection on ' + this.props.rootDoc.title });
let minX = Number.MAX_VALUE;
let maxX = -Number.MAX_VALUE;
let minY = Number.MAX_VALUE;
@@ -271,7 +272,8 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
width: `${this._width}px`,
height: `${this._height}px`,
border: `${this._width === 0 ? '' : '2px dashed black'}`,
- }}/>
+ }}
+ />
);
}
}
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 12f41394d..df1eb72ce 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -15,6 +15,7 @@ import { SearchBox } from './search/SearchBox';
import './SidebarAnnos.scss';
import { StyleProp } from './StyleProvider';
import React = require('react');
+import { RichTextField } from '../../fields/RichTextField';
interface ExtraProps {
fieldKey: string;
@@ -72,6 +73,27 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
FormattedTextBox.DontSelectInitialText = true;
this.allMetadata.map(tag => (target[tag] = tag));
DocUtils.MakeLink({ doc: anchor }, { doc: target }, 'inline comment:comment on');
+
+ Doc.GetProto(target).text = new RichTextField(
+ JSON.stringify({
+ doc: {
+ type: 'doc',
+ content: [
+ {
+ type: 'paragraph',
+ attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
+ content: [{ type: 'dashField', attrs: { fieldKey: 'text', docid: anchor[Id], hideKey: true, editable: false } }],
+ },
+ {
+ type: 'paragraph',
+ attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
+ },
+ ],
+ },
+ selection: { type: 'text', anchor: 4, head: 4 },
+ }),
+ ''
+ );
this.addDocument(target);
this._stackRef.current?.focusDocument(target, {});
return target;
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 869570a17..5abf4bde2 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -288,11 +288,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
return undefined;
case StyleProp.Decorations:
if (props?.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform || doc?.x !== undefined || doc?.y !== undefined) {
- return doc &&
- isBackground() &&
- !Doc.IsSystem(doc) &&
- (props?.renderDepth || 0) > 0 &&
- ((doc.type === DocumentType.COL && doc._viewType !== CollectionViewType.Pile) || [DocumentType.RTF, DocumentType.IMG, DocumentType.INK].includes(doc.type as DocumentType)) ? (
+ return doc && isBackground() && !Doc.IsSystem(doc) && (props?.renderDepth || 0) > 0 ? (
<div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}>
<FontAwesomeIcon icon={isBackground() ? 'lock' : 'unlock'} style={{ color: isBackground() ? 'red' : undefined }} size="lg" />
</div>
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index ac3541f2c..78e44dfa2 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -89,6 +89,10 @@
position: relative;
}
+.lm_stack {
+ position: relative;
+}
+
.lm_drag_tab {
padding: 0;
width: 15px !important;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index e9b41de25..92319d080 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,6 +1,6 @@
import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
import * as GoldenLayout from '../../../client/goldenLayout';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
@@ -318,7 +318,7 @@ export class CollectionDockingView extends CollectionSubView() {
}
};
- componentDidMount: () => void = () => {
+ componentDidMount: () => void = async () => {
if (this._containerRef.current) {
this._lightboxReactionDisposer = reaction(
() => LightboxView.LightboxDoc,
@@ -335,8 +335,11 @@ export class CollectionDockingView extends CollectionSubView() {
this._ignoreStateChange = '';
}
);
- setTimeout(this.setupGoldenLayout);
- //window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window
+ reaction(
+ () => this.props.PanelWidth(),
+ width => !this._goldenLayout && width > 20 && setTimeout(() => this.setupGoldenLayout()), // need to wait for the collectiondockingview-container to have it's width/height since golden layout reads that to configure its windows
+ { fireImmediately: true }
+ );
}
};
@@ -460,7 +463,7 @@ export class CollectionDockingView extends CollectionSubView() {
};
tabDestroyed = (tab: any) => {
- if (![DocumentType.KVP, DocumentType.PRES].includes(tab.DashDoc?.type)) {
+ if (tab.DashDoc && ![DocumentType.KVP, DocumentType.PRES].includes(tab.DashDoc?.type)) {
Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc);
Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
}
@@ -470,8 +473,8 @@ export class CollectionDockingView extends CollectionSubView() {
Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc);
this.tabMap.delete(tab);
tab._disposers && Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
- tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele));
- setTimeout(this.stateChanged);
+ //tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele));
+ this.stateChanged();
}
};
tabCreated = (tab: any) => {
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index cde5132a3..31ed5a83b 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -4,7 +4,7 @@ import { Tooltip } from '@material-ui/core';
import { clamp } from 'lodash';
import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
import { DataSym, Doc, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
@@ -143,8 +143,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />;
const closeIcon = <FontAwesomeIcon icon={'eye'} />;
- ReactDOM.render(docIcon, iconWrap);
- ReactDOM.render(closeIcon, closeWrap);
+ ReactDOM.createRoot(iconWrap).render(docIcon);
+ ReactDOM.createRoot(closeWrap).render(closeIcon);
tab.reactComponents = [iconWrap, closeWrap];
tab.element[0].prepend(iconWrap);
tab._disposers.layerDisposer = reaction(
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 1d455ad08..627487a9e 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -40,9 +40,6 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
@observable public static AnnotationUri: string | undefined;
@observable public static LinkEditorDocView: DocumentView | undefined;
- @observable public static invisibleWebDoc: Opt<Doc>;
- public static invisibleWebRef = React.createRef<HTMLDivElement>();
-
@action
@undoBatch
onLinkButtonMoved = (e: PointerEvent) => {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index c7001f846..fcb3ccb07 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -74,7 +74,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (oldDiv.className === 'pdfBox-ui' || oldDiv.className === 'pdfViewerDash-overlay-inking') {
newDiv.style.display = 'none';
}
- if (newDiv && newDiv.style) newDiv.style.overflow = 'hidden';
+ if (newDiv?.style) newDiv.style.overflow = 'hidden';
if (oldDiv instanceof HTMLCanvasElement) {
const canvas = oldDiv;
const img = document.createElement('img'); // create a Image Element
@@ -398,9 +398,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
</div>
);
}
- sidebarWidth = () =>
- !this.SidebarShown ? 0 : PDFBox.sidebarResizerWidth + (this._previewWidth ? PDFBox.openSidebarWidth : ((NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth()) / NumCast(this.layoutDoc.nativeWidth));
-
+ sidebarWidth = () => {
+ if (!this.SidebarShown) return 0;
+ if (this._previewWidth) return PDFBox.sidebarResizerWidth + PDFBox.openSidebarWidth; // return default sidebar if previewing (as in viewing a link target)
+ const nativeDiff = NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc);
+ return PDFBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1);
+ };
specificContextMenu = (e: React.MouseEvent): void => {
const funcs: ContextMenuProps[] = [];
funcs.push({ description: 'Copy path', event: () => this.pdfUrl && Utils.CopyText(Utils.prepend('') + this.pdfUrl.url.pathname), icon: 'expand-arrows-alt' });
@@ -483,7 +486,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
crop={this.crop}
/>
</div>
- <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.layoutDoc[WidthSym]()}%` }}>
+ <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.props.PanelWidth()}%` }}>
<SidebarAnnos
ref={this._sidebarRef}
{...this.props}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index db493934a..5ce6a0eb1 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -11,7 +11,7 @@ import { listSpec } from '../../../fields/Schema';
import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField, WebField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils';
+import { emptyFunction, getWordAtPoint, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SnappingManager } from '../../util/SnappingManager';
@@ -836,9 +836,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
}
});
- sidebarWidth = () =>
- !this.SidebarShown ? 0 : WebBox.sidebarResizerWidth + (this._previewWidth ? WebBox.openSidebarWidth : ((NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth()) / NumCast(this.layoutDoc.nativeWidth));
-
+ sidebarWidth = () => {
+ if (!this.SidebarShown) return 0;
+ if (this._previewWidth) return WebBox.sidebarResizerWidth + WebBox.openSidebarWidth; // return default sidebar if previewing (as in viewing a link target)
+ const nativeDiff = NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc);
+ return WebBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1);
+ };
@computed get content() {
const interactive =
!this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
@@ -1000,6 +1003,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
docView={this.props.docViewPath().lastElement()}
finishMarquee={this.finishMarquee}
savedAnnotations={this.savedAnnotationsCreator}
+ selectionText={returnEmptyString}
annotationLayer={this._annotationLayer.current}
mainCont={this._mainCont.current}
/>{' '}
@@ -1015,7 +1019,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}}
onPointerDown={e => this.sidebarBtnDown(e, false)}
/>
- <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.layoutDoc[WidthSym]()}%` }}>
+ <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.props.PanelWidth()}%` }}>
<SidebarAnnos
ref={this._sidebarRef}
{...this.props}
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 2b83e9da8..42d20ba99 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -1,7 +1,6 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
-import Color from 'color';
import { action, computed, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -12,7 +11,7 @@ import { ScriptField } from '../../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { WebField } from '../../../../fields/URLField';
import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
-import { aggregateBounds, Utils } from '../../../../Utils';
+import { aggregateBounds, StopEvent, Utils } from '../../../../Utils';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
import { SelectionManager } from '../../../util/SelectionManager';
@@ -382,12 +381,15 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
// style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
// <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" />
// </div>;
- setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView
+ //setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView
return (
<div
className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')} ${this.colorPickerClosed}`}
style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
- onClick={action(() => (this.colorPickerClosed = !this.colorPickerClosed))}
+ onClick={action(e => {
+ this.colorPickerClosed = !this.colorPickerClosed;
+ e.stopPropagation();
+ })}
onPointerDown={e => e.stopPropagation()}>
{this.Icon(color)}
<div className="colorButton-color" style={{ backgroundColor: curColor }} />
@@ -395,7 +397,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
{/* {dropdownCaret} */}
{this.colorPickerClosed ? null : (
<div>
- <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()} onClick={e => e.stopPropagation()}>
+ <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onClick={e => e.stopPropagation()}>
{this.colorPicker(curColor)}
</div>
<div
diff --git a/src/client/views/nodes/formattedText/DashDocCommentView.tsx b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
index 40dd6fbc7..85523c176 100644
--- a/src/client/views/nodes/formattedText/DashDocCommentView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
@@ -1,5 +1,5 @@
import { TextSelection } from 'prosemirror-state';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
import { Doc } from '../../../../fields/Doc';
import { DocServer } from '../../../DocServer';
import React = require('react');
@@ -9,6 +9,7 @@ import React = require('react');
// the comment can be toggled on/off with the '<-' text anchor.
export class DashDocCommentView {
dom: HTMLDivElement; // container for label and value
+ root: any;
constructor(node: any, view: any, getPos: any) {
this.dom = document.createElement('div');
@@ -30,12 +31,13 @@ export class DashDocCommentView {
e.stopPropagation();
};
- ReactDOM.render(<DashDocCommentViewInternal view={view} getPos={getPos} docid={node.attrs.docid} />, this.dom);
+ this.root = ReactDOM.createRoot(this.dom);
+ this.root.render(<DashDocCommentViewInternal view={view} getPos={getPos} docid={node.attrs.docid} />);
(this as any).dom = this.dom;
}
destroy() {
- ReactDOM.unmountComponentAtNode(this.dom);
+ // ReactDOM.unmountComponentAtNode(this.dom);
}
selectNode() {}
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 5f576be41..63ee7d1f3 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -1,7 +1,7 @@
import { action, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { NodeSelection } from 'prosemirror-state';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
import { Doc, HeightSym, WidthSym } from '../../../../fields/Doc';
import { Cast, NumCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnFalse, Utils } from '../../../../Utils';
@@ -15,6 +15,7 @@ import React = require('react');
export class DashDocView {
dom: HTMLSpanElement; // container for label and value
+ root: any;
constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
this.dom = document.createElement('span');
@@ -38,14 +39,13 @@ export class DashDocView {
e.stopPropagation();
};
- ReactDOM.render(
- <DashDocViewInternal docid={node.attrs.docid} alias={node.attrs.alias} width={node.attrs.width} height={node.attrs.height} hidden={node.attrs.hidden} fieldKey={node.attrs.fieldKey} tbox={tbox} view={view} node={node} getPos={getPos} />,
- this.dom
+ this.root = ReactDOM.createRoot(this.dom);
+ this.root.render(
+ <DashDocViewInternal docid={node.attrs.docid} alias={node.attrs.alias} width={node.attrs.width} height={node.attrs.height} hidden={node.attrs.hidden} fieldKey={node.attrs.fieldKey} tbox={tbox} view={view} node={node} getPos={getPos} />
);
- (this as any).dom = this.dom;
}
destroy() {
- ReactDOM.unmountComponentAtNode(this.dom);
+ // ReactDOM.unmountComponentAtNode(this.dom);
}
selectNode() {}
}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 35d919f38..f088b39d1 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,6 +1,6 @@
import { action, computed, IReactionDisposer, observable } from 'mobx';
import { observer } from 'mobx-react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
import { DataSym, Doc, DocListCast, Field } from '../../../../fields/Doc';
import { List } from '../../../../fields/List';
import { listSpec } from '../../../../fields/Schema';
@@ -19,6 +19,7 @@ import { CollectionViewType } from '../../../documents/DocumentTypes';
export class DashFieldView {
dom: HTMLDivElement; // container for label and value
+ root: any;
constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
const { boolVal, strVal } = DashFieldViewInternal.fieldContent(tbox.props.Document, tbox.rootDoc, node.attrs.fieldKey);
@@ -43,11 +44,11 @@ export class DashFieldView {
e.stopPropagation();
};
- setTimeout(() => ReactDOM.render(<DashFieldViewInternal fieldKey={node.attrs.fieldKey} docid={node.attrs.docid} width={node.attrs.width} height={node.attrs.height} hideKey={node.attrs.hideKey} tbox={tbox} />, this.dom));
- (this as any).dom = this.dom;
+ this.root = ReactDOM.createRoot(this.dom);
+ this.root.render(<DashFieldViewInternal fieldKey={node.attrs.fieldKey} docid={node.attrs.docid} width={node.attrs.width} height={node.attrs.height} hideKey={node.attrs.hideKey} editable={node.attrs.editable} tbox={tbox} />);
}
destroy() {
- ReactDOM.unmountComponentAtNode(this.dom);
+ //this.root.unmount();
}
selectNode() {}
}
@@ -59,6 +60,7 @@ interface IDashFieldViewInternal {
tbox: FormattedTextBox;
width: number;
height: number;
+ editable: boolean;
}
@observer
@@ -116,7 +118,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
return (
<span
className="dashFieldView-fieldSpan"
- contentEditable={true}
+ contentEditable={this.props.editable}
style={{ display: strVal.length < 2 ? 'inline-block' : undefined }}
suppressContentEditableWarning={true}
defaultValue={strVal}
@@ -161,6 +163,41 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
if (nodeText) {
const newText = nodeText.startsWith(':=') || nodeText.startsWith('=:=') ? ':=-computed-' : nodeText;
+ // const json = {
+ // doc: {
+ // type: 'doc',
+ // content: [
+ // {
+ // type: 'paragraph',
+ // attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
+ // content: [{ type: 'dashField', attrs: { fieldKey: 'text', docid: '7e02a420-8add-49b0-ad20-54680567575f', hideKey: true, editable: false }, marks: [{ type: 'strong' }] }],
+ // },
+ // {
+ // type: 'paragraph',
+ // attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
+ // content: [{ type: 'text', marks: [{ type: 'user_mark', attrs: { userid: 'mart@bar.com', modified: 1667334077 } }] }],
+ // },
+ // ],
+ // },
+ // };
+
+ // const json = {
+ // doc: {
+ // type: 'doc',
+ // content: [
+ // {
+ // type: 'paragraph',
+ // attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
+ // content: [{ type: 'dashField', attrs: { fieldKey: 'hello', docid: '', hideKey: true, editable: true }, marks: [{ type: 'strong' }, { type: 'user_mark', attrs: { userid: 'mart@bar.com', modified: 1667334006 } }] }],
+ // },
+ // {
+ // type: 'paragraph',
+ // attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
+ // content: [{ type: 'text', marks: [{ type: 'user_mark', attrs: { userid: 'mart@bar.com', modified: 1667334088 } }] }],
+ // },
+ // ],
+ // },
+ // };
// look for a document whose id === the fieldKey being displayed. If there's a match, then that document
// holds the different enumerated values for the field in the titles of its collected documents.
// if there's a partial match from the start of the input text, complete the text --- TODO: make this an auto suggest box and select from a drop down.
@@ -266,14 +303,12 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
document.addEventListener('pointerdown', hideMenu, true);
};
render() {
- const buttons = [
+ return this.getElement([
<Tooltip key="trash" title={<div className="dash-tooltip">{`Show Pivot Viewer for '${this._fieldKey}'`}</div>}>
<button className="antimodeMenu-button" onPointerDown={this.showFields}>
<FontAwesomeIcon icon="eye" size="lg" />
</button>
</Tooltip>,
- ];
-
- return this.getElement(buttons);
+ ]);
}
}
diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx
index 4895dcdc5..0fd2a7808 100644
--- a/src/client/views/nodes/formattedText/EquationView.tsx
+++ b/src/client/views/nodes/formattedText/EquationView.tsx
@@ -2,7 +2,7 @@ import EquationEditor from 'equation-editor-react';
import { IReactionDisposer } from 'mobx';
import { observer } from 'mobx-react';
import { TextSelection } from 'prosemirror-state';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
import { Doc } from '../../../../fields/Doc';
import { StrCast } from '../../../../fields/Types';
import './DashFieldView.scss';
@@ -11,7 +11,7 @@ import React = require('react');
export class EquationView {
dom: HTMLDivElement; // container for label and value
-
+ root: any;
constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
this.dom = document.createElement('div');
this.dom.style.width = node.attrs.width;
@@ -22,13 +22,13 @@ export class EquationView {
e.stopPropagation();
};
- ReactDOM.render(<EquationViewInternal fieldKey={node.attrs.fieldKey} width={node.attrs.width} height={node.attrs.height} getPos={getPos} setEditor={this.setEditor} tbox={tbox} />, this.dom);
- (this as any).dom = this.dom;
+ this.root = ReactDOM.createRoot(this.dom);
+ this.root.render(<EquationViewInternal fieldKey={node.attrs.fieldKey} width={node.attrs.width} height={node.attrs.height} getPos={getPos} setEditor={this.setEditor} tbox={tbox} />);
}
_editor: EquationEditor | undefined;
setEditor = (editor?: EquationEditor) => (this._editor = editor);
destroy() {
- ReactDOM.unmountComponentAtNode(this.dom);
+ // ReactDOM.unmountComponentAtNode(this.dom);
}
setSelection() {
this._editor?.mathField.focus();
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 096f9a92c..0e48dd93a 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1132,6 +1132,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
);
quickScroll = undefined;
this.tryUpdateScrollHeight();
+ setTimeout(this.tryUpdateScrollHeight, 250);
}
pushToGoogleDoc = async () => {
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 47833dd43..dc5d8ada8 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -248,8 +248,7 @@ export class RichTextRules {
// [[fieldKey:Doc]] => show field of doc
new InputRule(new RegExp(/\[\[([a-zA-Z_\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@:\.\? \-0-9]+)?\]\]$/), (state, match, start, end) => {
const fieldKey = match[1];
- const rawdocid = match[3];
- const docid = rawdocid ? normalizeEmail(!rawdocid.includes('@') ? Doc.CurrentUserEmail + rawdocid : rawdocid.substring(1)) : undefined;
+ const docid = match[3]?.replace(':', '');
const value = match[2]?.substring(1);
if (!fieldKey) {
if (docid) {
@@ -259,7 +258,7 @@ export class RichTextRules {
if (rstate) {
this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3))));
}
- const target = (docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: rawdocid.replace(/^:/, ''), _width: 500, _height: 500 }, docid);
+ const target = (docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500 }, docid);
DocUtils.MakeLink({ doc: this.TextBox.getAnchor() }, { doc: target }, 'portal to:portal from', undefined);
const fstate = this.TextBox.EditorView?.state;
@@ -275,7 +274,7 @@ export class RichTextRules {
const num = value.match(/^[0-9.]$/);
this.Document[DataSym][fieldKey] = value === 'true' ? true : value === 'false' ? false : num ? Number(value) : value;
}
- const fieldView = state.schema.nodes.dashField.create({ fieldKey, docid });
+ const fieldView = state.schema.nodes.dashField.create({ fieldKey, docid, hideKey: true });
return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
}),
diff --git a/src/client/views/nodes/formattedText/SummaryView.tsx b/src/client/views/nodes/formattedText/SummaryView.tsx
index 01acc3de9..1fe6d822b 100644
--- a/src/client/views/nodes/formattedText/SummaryView.tsx
+++ b/src/client/views/nodes/formattedText/SummaryView.tsx
@@ -1,6 +1,6 @@
import { TextSelection } from 'prosemirror-state';
import { Fragment, Node, Slice } from 'prosemirror-model';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
import React = require('react');
// an elidable textblock that collapses when its '<-' is clicked and expands when its '...' anchor is clicked.
@@ -9,6 +9,7 @@ import React = require('react');
// method instead of changing prosemirror's text when the expand/elide buttons are clicked.
export class SummaryView {
dom: HTMLSpanElement; // container for label and value
+ root: any;
constructor(node: any, view: any, getPos: any) {
const self = this;
@@ -35,13 +36,13 @@ export class SummaryView {
return js.apply(this, arguments);
};
- ReactDOM.render(<SummaryViewInternal />, this.dom);
- (this as any).dom = this.dom;
+ this.root = ReactDOM.createRoot(this.dom);
+ this.root.render(<SummaryViewInternal />);
}
className = (visible: boolean) => 'formattedTextBox-summarizer' + (visible ? '' : '-collapsed');
destroy() {
- ReactDOM.unmountComponentAtNode(this.dom);
+ // ReactDOM.unmountComponentAtNode(this.dom);
}
selectNode() {}
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 66d747bf7..aa2475dca 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -263,6 +263,7 @@ export const nodes: { [index: string]: NodeSpec } = {
fieldKey: { default: '' },
docid: { default: '' },
hideKey: { default: false },
+ editable: { default: true },
},
group: 'inline',
draggable: false,
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 822af6a68..470aa3eb1 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -27,14 +27,14 @@
opacity: unset;
mix-blend-mode: multiply; // bcz: makes text fuzzy!
- span {
- padding-right: 5px;
- padding-bottom: 4px;
- }
+ // span {
+ // padding-right: 5px;
+ // padding-bottom: 4px;
+ // }
}
.textLayer ::selection {
- background: #ACCEF7;
+ background: #accef7;
}
// should match the backgroundColor in createAnnotation()
@@ -111,4 +111,4 @@
.pdfViewerDash-interactive {
pointer-events: all;
-} \ No newline at end of file
+}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 5c10c7cef..abc7336bd 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -79,6 +79,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
private _initialScroll: Opt<number>;
private _forcedScroll = true;
+ selectionText = () => this._selectionText;
+
@observable isAnnotating = false;
// key where data is stored
@computed get allAnnotations() {
@@ -358,7 +360,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
this._marqueeing = [e.clientX, e.clientY];
this.isAnnotating = true;
- if (e.target && ((e.target as any).className.includes('endOfContent') || (e.target as any).parentElement.className !== 'textLayer')) {
+ 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;
document.addEventListener('pointermove', this.onSelectMove); // need this to prevent document from being dragged if stopPropagation doesn't get called
} else {
@@ -574,6 +577,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
docView={this.props.docViewPath().lastElement()}
finishMarquee={this.finishMarquee}
savedAnnotations={this.savedAnnotations}
+ selectionText={this.selectionText}
annotationLayer={this._annotationLayer.current}
mainCont={this._mainCont.current}
anchorMenuCrop={this._textSelecting ? undefined : this.crop}
diff --git a/src/debug/Repl.tsx b/src/debug/Repl.tsx
index 1b12e208c..b8081648f 100644
--- a/src/debug/Repl.tsx
+++ b/src/debug/Repl.tsx
@@ -1,5 +1,5 @@
import * as React from 'react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
import { observer } from 'mobx-react';
import { observable, computed } from 'mobx';
import { CompileScript } from '../client/util/Scripting';
@@ -11,39 +11,40 @@ import { resolvedPorts } from '../client/util/CurrentUserUtils';
@observer
class Repl extends React.Component {
- @observable text: string = "";
+ @observable text: string = '';
- @observable executedCommands: { command: string, result: any }[] = [];
+ @observable executedCommands: { command: string; result: any }[] = [];
onChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
this.text = e.target.value;
- }
+ };
onKeyDown = (e: React.KeyboardEvent) => {
- if (!e.ctrlKey && e.key === "Enter") {
+ if (!e.ctrlKey && e.key === 'Enter') {
e.preventDefault();
const script = CompileScript(this.text, {
- addReturn: true, typecheck: false,
- params: { makeInterface: "any" }
+ addReturn: true,
+ typecheck: false,
+ params: { makeInterface: 'any' },
});
if (!script.compiled) {
- this.executedCommands.push({ command: this.text, result: "Compile Error" });
+ this.executedCommands.push({ command: this.text, result: 'Compile Error' });
} else {
const result = script.run({ makeInterface }, e => this.executedCommands.push({ command: this.text, result: e.message || e }));
result.success && this.executedCommands.push({ command: this.text, result: result.result });
}
- this.text = "";
+ this.text = '';
}
- }
+ };
@computed
get commands() {
return this.executedCommands.map(command => {
return (
- <div style={{ marginTop: "5px" }}>
+ <div style={{ marginTop: '5px' }}>
<p>{command.command}</p>
{/* <pre>{JSON.stringify(command.result, null, 2)}</pre> */}
- <pre>{command.result instanceof RefField || command.result instanceof ObjectField ? "object" : String(command.result)}</pre>
+ <pre>{command.result instanceof RefField || command.result instanceof ObjectField ? 'object' : String(command.result)}</pre>
</div>
);
});
@@ -52,16 +53,14 @@ class Repl extends React.Component {
render() {
return (
<div>
- <div style={{ verticalAlign: "bottom" }}>
- {this.commands}
- </div>
- <textarea style={{ width: "100%", position: "absolute", bottom: "0px" }} value={this.text} onChange={this.onChange} onKeyDown={this.onKeyDown} />
+ <div style={{ verticalAlign: 'bottom' }}>{this.commands}</div>
+ <textarea style={{ width: '100%', position: 'absolute', bottom: '0px' }} value={this.text} onChange={this.onChange} onKeyDown={this.onKeyDown} />
</div>
);
}
}
(async function () {
- DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, "repl");
- ReactDOM.render(<Repl />, document.getElementById("root"));
-})(); \ No newline at end of file
+ DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, 'repl');
+ ReactDOM.createRoot(document.getElementById('root')!).render(<Repl />);
+})();