aboutsummaryrefslogtreecommitdiff
path: root/src/client/views
diff options
context:
space:
mode:
authorLionel Han <47760119+IGoByJoe@users.noreply.github.com>2020-08-08 21:32:51 -0700
committerLionel Han <47760119+IGoByJoe@users.noreply.github.com>2020-08-08 21:32:51 -0700
commit98d753d5dd66a646f00bcdf15656f845cd27fb81 (patch)
tree402eda69cf59fb9a85814f02f57a1ff3862abbb8 /src/client/views
parent25fa93e7060c8f8561c294c7309c763e3b8f9313 (diff)
parent9f4a7ec5f79d9dec3a0ffb0b633197216cbefec8 (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into new_audio
Diffstat (limited to 'src/client/views')
-rw-r--r--src/client/views/GlobalKeyHandler.ts2
-rw-r--r--src/client/views/MainView.tsx93
-rw-r--r--src/client/views/PropertiesButtons.scss18
-rw-r--r--src/client/views/PropertiesButtons.tsx56
-rw-r--r--src/client/views/collections/CollectionLinearView.tsx2
-rw-r--r--src/client/views/collections/CollectionSubView.tsx32
-rw-r--r--src/client/views/collections/CollectionView.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/PropertiesView.scss66
-rw-r--r--src/client/views/collections/collectionFreeForm/PropertiesView.tsx83
-rw-r--r--src/client/views/linking/LinkEditor.tsx6
-rw-r--r--src/client/views/linking/LinkMenuItem.tsx33
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx122
-rw-r--r--src/client/views/nodes/DocumentView.tsx13
-rw-r--r--src/client/views/nodes/FontIconBox.scss2
-rw-r--r--src/client/views/nodes/FontIconBox.tsx5
-rw-r--r--src/client/views/nodes/PresBox.tsx3
17 files changed, 374 insertions, 166 deletions
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 3a61e89ce..0ea02e3cb 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -7,7 +7,6 @@ import { List } from "../../fields/List";
import { ScriptField } from "../../fields/ScriptField";
import { Cast, PromiseValue } from "../../fields/Types";
import GoogleAuthenticationManager from "../apis/GoogleAuthenticationManager";
-import HypothesisAuthenticationManager from "../apis/HypothesisAuthenticationManager";
import { DocServer } from "../DocServer";
import { DocumentType } from "../documents/DocumentTypes";
import { DictationManager } from "../util/DictationManager";
@@ -107,7 +106,6 @@ export default class KeyManager {
doDeselect && SelectionManager.DeselectAll();
DictationManager.Controls.stop();
GoogleAuthenticationManager.Instance.cancel();
- HypothesisAuthenticationManager.Instance.cancel();
SharingManager.Instance.close();
GroupManager.Instance.close();
CollectionFreeFormViewChrome.Instance.clearKeep();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b6058db7a..f5dccd567 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -14,9 +14,8 @@ import { listSpec } from '../../fields/Schema';
import { ScriptField } from '../../fields/ScriptField';
import { BoolCast, Cast, FieldValue, StrCast } from '../../fields/Types';
import { TraceMobx } from '../../fields/util';
-import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../Utils';
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils, simulateMouseClick } from '../../Utils';
import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
-import HypothesisAuthenticationManager from '../apis/HypothesisAuthenticationManager';
import { DocServer } from '../DocServer';
import { Docs, DocumentOptions } from '../documents/Documents';
import { DocumentType } from '../documents/DocumentTypes';
@@ -59,7 +58,10 @@ import { TaskCompletionBox } from './nodes/TaskCompletedBox';
import { OverlayView } from './OverlayView';
import PDFMenu from './pdf/PDFMenu';
import { PreviewCursor } from './PreviewCursor';
+import { Hypothesis } from '../util/HypothesisUtils';
import { undoBatch } from '../util/UndoManager';
+import { WebBox } from './nodes/WebBox';
+import * as ReactDOM from 'react-dom';
import { SearchBox } from './search/SearchBox';
@observer
@@ -127,6 +129,7 @@ export class MainView extends React.Component {
}
});
});
+ document.addEventListener("linkAnnotationToDash", Hypothesis.linkListener);
}
componentWillUnMount() {
@@ -134,6 +137,7 @@ export class MainView extends React.Component {
window.removeEventListener("pointerdown", this.globalPointerDown);
window.removeEventListener("pointerup", this.globalPointerUp);
window.removeEventListener("paste", KeyManager.Instance.paste as any);
+ document.removeEventListener("linkAnnotationToDash", Hypothesis.linkListener);
}
constructor(props: Readonly<{}>) {
@@ -772,6 +776,37 @@ export class MainView extends React.Component {
</div>;
}
+ @computed get invisibleWebBox() { // see note under the makeLink method in HypothesisUtils.ts
+ return !DocumentLinksButton.invisibleWebDoc ? null :
+ <div style={{ position: 'absolute', left: 50, top: 50, display: 'block', width: '500px', height: '1000px' }} ref={DocumentLinksButton.invisibleWebRef}>
+ <WebBox
+ fieldKey={"data"}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ Document={DocumentLinksButton.invisibleWebDoc}
+ LibraryPath={emptyPath}
+ dropAction={"move"}
+ isSelected={returnFalse}
+ select={returnFalse}
+ rootSelected={returnFalse}
+ renderDepth={0}
+ addDocTab={returnFalse}
+ pinToPres={returnFalse}
+ ScreenToLocalTransform={Transform.Identity}
+ bringToFront={returnFalse}
+ active={returnFalse}
+ whenActiveChanged={returnFalse}
+ focus={returnFalse}
+ PanelWidth={() => 500}
+ PanelHeight={() => 800}
+ NativeHeight={() => 500}
+ NativeWidth={() => 800}
+ ContentScaling={returnOne}
+ docFilters={returnEmptyFilter}
+ />
+ </div>;
+ }
+
render() {
return (<div className={"mainView-container" + (this.darkScheme ? "-dark" : "")} ref={this._mainViewRef}>
@@ -781,7 +816,6 @@ export class MainView extends React.Component {
<SettingsManager />
<GroupManager />
<GoogleAuthenticationManager />
- <HypothesisAuthenticationManager />
<DocumentDecorations />
{this.search}
<CollectionMenu />
@@ -806,8 +840,61 @@ export class MainView extends React.Component {
<OverlayView />
<TimelineMenu />
{this.snapLines}
+ <div ref={this.makeWebRef} style={{ position: 'absolute', left: -1000, top: -1000, display: 'block', width: '200px', height: '800px' }} />
</div >);
}
+
+ makeWebRef = (ele: HTMLDivElement) => {
+ reaction(() => DocumentLinksButton.invisibleWebDoc,
+ invisibleDoc => {
+ ReactDOM.unmountComponentAtNode(ele);
+ invisibleDoc && ReactDOM.render(<span title="Drag as document" className="invisible-webbox" >
+ <div style={{ position: 'absolute', left: -1000, top: -1000, display: 'block', width: '200px', height: '800px' }} ref={DocumentLinksButton.invisibleWebRef}>
+ <WebBox
+ fieldKey={"data"}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ Document={invisibleDoc}
+ LibraryPath={emptyPath}
+ dropAction={"move"}
+ isSelected={returnFalse}
+ select={returnFalse}
+ rootSelected={returnFalse}
+ renderDepth={0}
+ addDocTab={returnFalse}
+ pinToPres={returnFalse}
+ ScreenToLocalTransform={Transform.Identity}
+ bringToFront={returnFalse}
+ active={returnFalse}
+ whenActiveChanged={returnFalse}
+ focus={returnFalse}
+ PanelWidth={() => 500}
+ PanelHeight={() => 800}
+ NativeHeight={() => 500}
+ NativeWidth={() => 800}
+ ContentScaling={returnOne}
+ docFilters={returnEmptyFilter}
+ />
+ </div>;
+ </span>, ele);
+
+ var 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);
+ });
+ }
}
Scripting.addGlobal(function freezeSidebar() { MainView.expandFlyout(); });
Scripting.addGlobal(function toggleComicMode() { Doc.UserDoc().fontFamily = "Comic Sans MS"; Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; });
diff --git a/src/client/views/PropertiesButtons.scss b/src/client/views/PropertiesButtons.scss
index 6199d34d0..8d9d56c9e 100644
--- a/src/client/views/PropertiesButtons.scss
+++ b/src/client/views/PropertiesButtons.scss
@@ -20,8 +20,8 @@ $linkGap : 3px;
.propertiesButtons-linkButton-empty,
.propertiesButtons-linkButton-nonempty {
- height: 30px;
- width: 32px;
+ height: 25px;
+ width: 29px;
border-radius: 6px;
pointer-events: auto;
background-color: #121721;
@@ -35,7 +35,7 @@ $linkGap : 3px;
justify-content: center;
align-items: center;
margin-right: 10px;
- margin-left: 3.5px;
+ margin-left: 4px;
&:hover {
background: $main-accent;
@@ -68,7 +68,7 @@ $linkGap : 3px;
padding-right: 5px;
width: 25px;
border-radius: 5px;
- margin-right: 22px;
+ margin-right: 20px;
margin-bottom: 8px;
}
@@ -76,9 +76,9 @@ $linkGap : 3px;
background: #121721;
color: white;
font-size: 6px;
- width: 40px;
+ width: 37px;
padding: 3px;
- height: 13px;
+ height: 12px;
border-radius: 7px;
text-transform: uppercase;
text-align: center;
@@ -86,8 +86,8 @@ $linkGap : 3px;
}
.propertiesButtons-linker {
- height: 30px;
- width: 32px;
+ height: 25px;
+ width: 29px;
text-align: center;
border-radius: 6px;
pointer-events: auto;
@@ -96,7 +96,7 @@ $linkGap : 3px;
transition: 0.2s ease all;
margin-right: 5px;
padding-top: 5px;
- margin-left: 3.5px;
+ margin-left: 4px;
&:hover {
background: $main-accent;
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 5c584d270..5e25ead87 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -3,7 +3,7 @@ import { faArrowAltCircleDown, faArrowAltCircleRight, faArrowAltCircleUp, faChec
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../fields/Doc";
+import { Doc, DocListCast, AclEdit, AclAdmin } from "../../fields/Doc";
import { RichTextField } from '../../fields/RichTextField';
import { Cast, NumCast, BoolCast } from "../../fields/Types";
import { emptyFunction, setupMoveUpEvents, Utils } from "../../Utils";
@@ -30,6 +30,7 @@ import { undoBatch, UndoManager } from '../util/UndoManager';
import { DocumentType } from '../documents/DocumentTypes';
import { InkField } from '../../fields/InkField';
import { PresBox } from './nodes/PresBox';
+import { GetEffectiveAcl } from "../../fields/util";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -224,10 +225,11 @@ export class PropertiesButtons extends React.Component<{}, {}> {
<FontAwesomeIcon className="documentdecorations-icon" size="lg" icon="map-pin" />
</div>
- <div className="propertiesButtons-title" style={{
- backgroundColor: Doc.isDocPinned(targetDoc) ? "white" : "black",
- color: Doc.isDocPinned(targetDoc) ? "black" : "white"
- }}
+ <div className="propertiesButtons-title"
+ // style={{
+ // backgroundColor: Doc.isDocPinned(targetDoc) ? "white" : "black",
+ // color: Doc.isDocPinned(targetDoc) ? "black" : "white"
+ // }}
>{Doc.isDocPinned(targetDoc) ? "Unpin" : "Pin"}</div>
</div>
</Tooltip>;
@@ -258,7 +260,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
}
}}>
<FontAwesomeIcon className="documentdecorations-icon" size="lg" icon="map-pin" />
- <div style={{ position: 'relative', fontSize: 25, fontWeight: 700, transform: 'translate(0, -20px)', color: 'rgba(250,250,250,0.5)' }}>V</div>
+ <div style={{ position: 'relative', fontSize: 25, fontWeight: 700, transform: 'translate(0, -28px)', color: 'rgba(250,250,250,0.55)' }}>V</div>
</div>
<div className="propertiesButtons-title">{"View"}</div>
@@ -381,10 +383,12 @@ export class PropertiesButtons extends React.Component<{}, {}> {
color={BoolCast(this.selectedDoc?.lockedPosition) ? "black" : "white"}
icon={BoolCast(this.selectedDoc?.lockedPosition) ? "unlock" : "lock"} size="lg" />}
</div>
- <div className="propertiesButtons-title" style={{
- backgroundColor: BoolCast(this.selectedDoc?.lockedPosition) ? "white" : "black",
- color: BoolCast(this.selectedDoc?.lockedPosition) ? "black" : "white"
- }}>Position </div>
+ <div className="propertiesButtons-title"
+ // style={{
+ // backgroundColor: BoolCast(this.selectedDoc?.lockedPosition) ? "white" : "black",
+ // color: BoolCast(this.selectedDoc?.lockedPosition) ? "black" : "white"
+ // }}
+ >Position </div>
</div>
</Tooltip>;
}
@@ -428,7 +432,19 @@ export class PropertiesButtons extends React.Component<{}, {}> {
@undoBatch
@action
deleteDocument = () => {
+ const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;
+ const selected = SelectionManager.SelectedDocuments().slice();
+
+ selected.map(dv => {
+ const effectiveAcl = GetEffectiveAcl(dv.props.Document);
+ if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) { // deletes whatever you have the right to delete
+ recent && Doc.AddDocToList(recent, "data", dv.props.Document, undefined, true, true);
+ dv.props.removeDocument?.(dv.props.Document);
+ }
+ });
+ this.selectedDoc && (this.selectedDoc.deleted = true);
this.selectedDocumentView?.props.ContainingCollectionView?.removeDocument(this.selectedDocumentView?.props.Document);
+ SelectionManager.DeselectAll();
}
@computed
@@ -582,10 +598,12 @@ export class PropertiesButtons extends React.Component<{}, {}> {
color={this.selectedDoc?.useClusters ? "black" : "white"}
icon="braille" size="lg" />}
</div>
- <div className="propertiesButtons-title" style={{
- backgroundColor: this.selectedDoc?.useClusters ? "white" : "black",
- color: this.selectedDoc?.useClusters ? "black" : "white"
- }}> clusters </div>
+ <div className="propertiesButtons-title"
+ // style={{
+ // backgroundColor: this.selectedDoc?.useClusters ? "white" : "black",
+ // color: this.selectedDoc?.useClusters ? "black" : "white"
+ // }}
+ > clusters </div>
</div>
</Tooltip>;
}
@@ -613,10 +631,12 @@ export class PropertiesButtons extends React.Component<{}, {}> {
color={this.selectedDoc?._fitToBox ? "black" : "white"}
icon="expand" size="lg" />}
</div>
- <div className="propertiesButtons-title" style={{
- backgroundColor: this.selectedDoc?._fitToBox ? "white" : "black",
- color: this.selectedDoc?._fitToBox ? "black" : "white"
- }}> {this.selectedDoc?._fitToBox ? "unfit" : "fit"} </div>
+ <div className="propertiesButtons-title"
+ // style={{
+ // backgroundColor: this.selectedDoc?._fitToBox ? "white" : "black",
+ // color: this.selectedDoc?._fitToBox ? "black" : "white"
+ // }}
+ > {this.selectedDoc?._fitToBox ? "unfit" : "fit"} </div>
</div>
</Tooltip>;
}
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index a9e812ad3..3cf46dbed 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -176,7 +176,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
}}
onPointerDown={e => e.stopPropagation()} >
<span className="bottomPopup-text" >
- Creating link from: {DocumentLinksButton.StartLink.props.Document.title}
+ Creating link from: {DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'}
</span>
<Tooltip title={<><div className="dash-tooltip">{LinkDescriptionPopup.showDescriptions ? "Turn off description pop-up" :
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 0e40cd21c..72aece284 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -402,14 +402,28 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
// }
}
if (uriList) {
- this.addDocument(Docs.Create.WebDocument(uriList, {
- ...options,
- title: uriList,
- _width: 400,
- _height: 315,
- _nativeWidth: 850,
- _nativeHeight: 962
- }));
+ const existingWebDoc = await Hypothesis.findWebDoc(uriList);
+ if (existingWebDoc) {
+ const alias = Doc.MakeAlias(existingWebDoc);
+ alias.x = options.x;
+ alias.y = options.y;
+ alias._nativeWidth = 850;
+ alias._nativeHeight = 962;
+ alias._width = 400;
+ this.addDocument(alias);
+ } else {
+ const newDoc = Docs.Create.WebDocument(uriList, {
+ ...options,
+ title: uriList.split("#annotations:")[0],
+ _width: 400,
+ _height: 315,
+ _nativeWidth: 850,
+ _nativeHeight: 962,
+ UseCors: true
+ });
+ newDoc.data = new WebField(uriList.split("#annotations:")[0]); // clean hypothes.is URLs that reference a specific annotation (eg. https://en.wikipedia.org/wiki/Cartoon#annotations:t7qAeNbCEeqfG5972KR2Ig)
+ this.addDocument(newDoc);
+ }
return;
}
@@ -492,3 +506,5 @@ import { CollectionView, CollectionViewType } from "./CollectionView";
import { SelectionManager } from "../../util/SelectionManager";
import { OverlayView } from "../OverlayView";
import { setTimeout } from "timers";
+import { Hypothesis } from "../../util/HypothesisUtils";
+
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 4d1cb670c..837ae7e86 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -594,7 +594,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
""))}
{(Doc.UserDoc()?.noviceMode || !this.props.isSelected() && !this.props.Document.forceActive) || this.props.Document.hideFilterView ? (null) :
<div className="collectionView-filterDragger" title="library View Dragger" onPointerDown={this.onPointerDown}
- style={{ right: this.facetWidth() - 1, top: this.props.Document._viewType === CollectionViewType.Docking ? "25%" : "55%" }} />
+ style={{ right: this.facetWidth() - 1, top: this.props.Document._viewType === CollectionViewType.Docking ? "25%" : "60%" }} />
}
{Doc.UserDoc()?.noviceMode || this.facetWidth() < 10 ? (null) : this.filterView}
</div>);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 88fe03efd..858f33291 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -74,7 +74,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
if (e.key === "?") {
ContextMenu.Instance.setDefaultItem("?", (str: string) => {
const textDoc = Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, {
- _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 800, isAnnotating: false,
+ _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false,
title: "bing", UseCors: true
});
this.props.addDocTab(textDoc, "onRight");
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss
index 5e0c9fcbb..aee28366a 100644
--- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss
@@ -23,8 +23,9 @@
height: 20px;
padding-left: 38px;
margin-top: -5px;
- right: 19;
- position: absolute;
+ align-items: flex-end;
+ margin-left: auto;
+ margin-right: 10px;
&:hover {
color: grey;
@@ -66,9 +67,9 @@
.propertiesView-settings-title-icon {
float: right;
- right: 0;
- position: absolute;
- margin-left: 2px;
+ justify-items: right;
+ align-items: flex-end;
+ margin-left: auto;
margin-right: 9px;
&:hover {
@@ -104,9 +105,9 @@
.propertiesView-sharing-title-icon {
float: right;
- right: 0;
- position: absolute;
- margin-left: 2px;
+ justify-items: right;
+ align-items: flex-end;
+ margin-left: auto;
margin-right: 9px;
&:hover {
@@ -154,9 +155,9 @@
.propertiesView-appearance-title-icon {
float: right;
- right: 0;
- position: absolute;
- margin-left: 2px;
+ justify-items: right;
+ align-items: flex-end;
+ margin-left: auto;
margin-right: 9px;
&:hover {
@@ -191,9 +192,9 @@
.propertiesView-transform-title-icon {
float: right;
- right: 0;
- position: absolute;
- margin-left: 2px;
+ justify-items: right;
+ align-items: flex-end;
+ margin-left: auto;
margin-right: 9px;
&:hover {
@@ -285,9 +286,8 @@
.propertiesView-sharingTable-item-permission {
display: flex;
- right: 34;
- float: right;
- position: absolute;
+ align-items: flex-end;
+ margin-left: auto;
.permissions-select {
z-index: 1;
@@ -326,25 +326,18 @@
cursor: pointer;
}
- .propertiesView-fields-title-name {
- font-size: 12.5px;
- font-weight: bold;
- white-space: nowrap;
- width: 35px;
- display: flex;
- }
-
.propertiesView-fields-title-icon {
float: right;
- right: 0;
- position: absolute;
- margin-left: 2px;
+ justify-items: right;
+ align-items: flex-end;
+ margin-left: auto;
margin-right: 9px;
&:hover {
cursor: pointer;
}
}
+
}
.propertiesView-fields-checkbox {
@@ -407,9 +400,9 @@
.propertiesView-layout-title-icon {
float: right;
- right: 0;
- position: absolute;
- margin-left: 2px;
+ justify-items: right;
+ align-items: flex-end;
+ margin-left: auto;
margin-right: 9px;
&:hover {
@@ -444,9 +437,9 @@
.propertiesView-presTrails-title-icon {
float: right;
- right: 0;
- position: absolute;
- margin-left: 2px;
+ justify-items: right;
+ align-items: flex-end;
+ margin-left: auto;
margin-right: 9px;
&:hover {
@@ -730,4 +723,9 @@
&:hover {
border: 0.75px solid rgb(122, 28, 28);
}
+}
+
+
+.properties-flyout {
+ grid-column: 2/4;
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
index 1a8ee3ea1..b1c3d3dc5 100644
--- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
@@ -22,13 +22,13 @@ import { InkField } from "../../../../fields/InkField";
import { undoBatch, UndoManager } from "../../../util/UndoManager";
import { ColorState, SketchPicker } from "react-color";
import "./FormatShapePane.scss";
-import { discovery_v1 } from "googleapis";
import { PresBox } from "../../nodes/PresBox";
import { DocumentManager } from "../../../util/DocumentManager";
import FormatShapePane from "./FormatShapePane";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
+const _global = (window /* browser */ || global /* node */) as any;
// import * as fa from '@fortawesome/free-solid-svg-icons';
// import { library } from "@fortawesome/fontawesome-svg-core";
@@ -52,7 +52,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@computed get selectedDocumentView() {
if (SelectionManager.SelectedDocuments().length) {
return SelectionManager.SelectedDocuments()[0];
- } else if (PresBox.Instance._selectedArray.length) {
+ } else if (PresBox.Instance && PresBox.Instance._selectedArray.length) {
return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
} else { return undefined; }
}
@@ -247,12 +247,23 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
return false;
}
+ @observable transform: Transform = Transform.Identity();
+ getTransform = () => this.transform;
+ propertiesDocViewRef = (ref: HTMLDivElement) => {
+ const observer = new _global.ResizeObserver(action((entries: any) => {
+ const cliRect = ref.getBoundingClientRect();
+ this.transform = new Transform(-cliRect.x, -cliRect.y, 1);
+ }));
+ ref && observer.observe(ref);
+ }
+
+ previewBackground = () => "lightgrey";
@computed get layoutPreview() {
if (this.selectedDoc) {
const layoutDoc = Doc.Layout(this.selectedDoc);
const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.docHeight;
const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.docWidth;
- return <div style={{ display: "inline-block", height: panelHeight() }} key={this.selectedDoc[Id]}>
+ return <div ref={this.propertiesDocViewRef} style={{ pointerEvents: "none", display: "inline-block", height: panelHeight() }} key={this.selectedDoc[Id]}>
<ContentFittingDocumentView
Document={layoutDoc}
DataDoc={this.dataDoc}
@@ -260,7 +271,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
renderDepth={this.props.renderDepth + 1}
rootSelected={returnFalse}
treeViewDoc={undefined}
- backgroundColor={() => "lightgrey"}
+ backgroundColor={this.previewBackground}
fitToBox={true}
FreezeDimensions={true}
NativeWidth={layoutDoc.type ===
@@ -270,7 +281,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
PanelWidth={panelWidth}
PanelHeight={panelHeight}
focus={returnFalse}
- ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+ ScreenToLocalTransform={this.getTransform}
docFilters={returnEmptyFilter}
ContainingCollectionDoc={undefined}
ContainingCollectionView={undefined}
@@ -320,7 +331,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
* @returns the notification icon. On clicking, it should notify someone of a document been shared with them.
*/
@computed get notifyIcon() {
- return <Tooltip title={<><div className="dash-tooltip">Notify with message</div></>}>
+ return <Tooltip title={<div className="dash-tooltip">Notify with message</div>}>
<div className="notify-button">
<FontAwesomeIcon className="notify-button-icon" icon="bell" color="white" size="sm" />
</div>
@@ -331,7 +342,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
* ... next to the owner that opens the main SharingManager interface on click.
*/
@computed get expansionIcon() {
- return <Tooltip title={<><div className="dash-tooltip">{"Show more permissions"}</div></>}>
+ return <Tooltip title={<div className="dash-tooltip">{"Show more permissions"}</div>}>
<div className="expansion-button" onPointerDown={() => {
if (this.selectedDocumentView) {
SharingManager.Instance.open(this.selectedDocumentView);
@@ -346,7 +357,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
* @returns a row of the permissions panel
*/
sharingItem(name: string, effectiveAcl: symbol, permission: string) {
- return <div className="propertiesView-sharingTable-item"
+ return <div className="propertiesView-sharingTable-item" key={name + permission}
// style={{ backgroundColor: this.selectedUser === name ? "#bcecfc" : "" }}
// onPointerDown={action(() => this.selectedUser = this.selectedUser === name ? "" : name)}
>
@@ -378,7 +389,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
if (this.selectedDoc![AclSym]) {
for (const [key, value] of Object.entries(this.selectedDoc![AclSym])) {
const name = key.substring(4).replace("_", ".");
- if (name !== Doc.CurrentUserEmail && name !== this.selectedDoc!.author/* && sidebarUsersDisplayed![name] !== false*/) tableEntries.push(this.sharingItem(name, effectiveAcl, AclMap.get(value)!));
+ if (name !== Doc.CurrentUserEmail && name !== this.selectedDoc!.author/* && sidebarUsersDisplayed![name] !== false*/) {
+ tableEntries.push(this.sharingItem(name, effectiveAcl, AclMap.get(value)!));
+ }
}
}
@@ -484,17 +497,17 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@computed
get controlPointsButton() {
return <div className="inking-button">
- <Tooltip title={<><div className="dash-tooltip">{"Edit points"}</div></>}>
+ <Tooltip title={<div className="dash-tooltip">{"Edit points"}</div>}>
<div className="inking-button-points" onPointerDown={action(() => FormatShapePane.Instance._controlBtn = !FormatShapePane.Instance._controlBtn)} style={{ backgroundColor: FormatShapePane.Instance._controlBtn ? "black" : "" }}>
<FontAwesomeIcon icon="bezier-curve" color="white" size="lg" />
</div>
</Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{FormatShapePane.Instance._lock ? "Unlock ratio" : "Lock ratio"}</div></>}>
+ <Tooltip title={<div className="dash-tooltip">{FormatShapePane.Instance._lock ? "Unlock ratio" : "Lock ratio"}</div>}>
<div className="inking-button-lock" onPointerDown={action(() => FormatShapePane.Instance._lock = !FormatShapePane.Instance._lock)} >
<FontAwesomeIcon icon={FormatShapePane.Instance._lock ? "lock" : "unlock"} color="white" size="lg" />
</div>
</Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{"Rotate 90˚"}</div></>}>
+ <Tooltip title={<div className="dash-tooltip">{"Rotate 90˚"}</div>}>
<div className="inking-button-rotate" onPointerDown={action(() => this.rotate(Math.PI / 2))}>
<FontAwesomeIcon icon="undo" color="white" size="lg" />
</div>
@@ -632,16 +645,19 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
set colorStk(value) { value && (this._lastLine = value); this.selectedDoc && (this.selectedDoc.color = value ? value : undefined); }
colorButton(value: string, type: string, setter: () => {}) {
- return <Flyout anchorPoint={anchorPoints.LEFT_TOP}
- content={type === "fill" ? this.fillPicker : this.linePicker}>
- <div className="color-button" key="color" onPointerDown={undoBatch(action(e => setter()))}>
- <div className="color-button-preview" style={{
- backgroundColor: value ?? "121212", width: 15, height: 15,
- display: value === "" || value === "transparent" ? "none" : ""
- }} />
- {value === "" || value === "transparent" ? <p style={{ fontSize: 25, color: "red", marginTop: -14, position: "fixed" }}>☒</p> : ""}
- </div>
- </Flyout>;
+ // return <div className="properties-flyout" onPointerEnter={e => this.changeScrolling(false)}
+ // onPointerLeave={e => this.changeScrolling(true)}>
+ // <Flyout anchorPoint={anchorPoints.LEFT_TOP}
+ // content={type === "fill" ? this.fillPicker : this.linePicker}>
+ return <div className="color-button" key="color" onPointerDown={undoBatch(action(e => setter()))}>
+ <div className="color-button-preview" style={{
+ backgroundColor: value ?? "121212", width: 15, height: 15,
+ display: value === "" || value === "transparent" ? "none" : ""
+ }} />
+ {value === "" || value === "transparent" ? <p style={{ fontSize: 25, color: "red", marginTop: -14 }}>☒</p> : ""}
+ </div>;
+ // </Flyout>
+ // </div>;
}
@@ -686,8 +702,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="stroke-button">{this.lineButton}</div>
</div>
</div>
- {/* {this._fillBtn ? this.fillPicker : ""}
- {this._lineBtn ? this.linePicker : ""} */}
+ {this._fillBtn ? this.fillPicker : ""}
+ {this._lineBtn ? this.linePicker : ""}
</div>;
}
@@ -813,7 +829,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
if (this.selectedDoc && !this.isPres) {
return <div className="propertiesView" style={{
width: this.props.width,
- // overflowY: this.inActions ? "visible" : "scroll"
+ //overflowY: this.scrolling ? "scroll" : "visible"
}} >
<div className="propertiesView-title" style={{ width: this.props.width }}>
Properties
@@ -918,11 +934,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="propertiesView-fields-title"
onPointerDown={() => runInAction(() => { this.openFields = !this.openFields; })}
style={{ backgroundColor: this.openFields ? "black" : "" }}>
- <div className="propertiesView-fields-title-name">
- Fields {"&"} Tags
+ Fields {"&"} Tags
<div className="propertiesView-fields-title-icon">
- <FontAwesomeIcon icon={this.openFields ? "caret-down" : "caret-right"} size="lg" color="white" />
- </div>
+ <FontAwesomeIcon icon={this.openFields ? "caret-down" : "caret-right"} size="lg" color="white" />
</div>
</div>
{!novice && this.openFields ? <div className="propertiesView-fields-checkbox">
@@ -943,18 +957,15 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<FontAwesomeIcon icon={this.openLayout ? "caret-down" : "caret-right"} size="lg" color="white" />
</div>
</div>
- {this.openLayout ? <div className="propertiesView-layout-content">{this.layoutPreview}</div> : null}
+ {this.openLayout ? <div className="propertiesView-layout-content" >{this.layoutPreview}</div> : null}
</div>
</div>;
}
if (this.isPres) {
- const selectedItem: boolean = PresBox.Instance._selectedArray.length > 0;
- return <div className="propertiesView" style={{ width: this.props.width }} >
- <div className="propertiesView-title" style={{ width: this.props.width }}>
+ const selectedItem: boolean = PresBox.Instance?._selectedArray.length > 0;
+ return <div className="propertiesView">
+ <div className="propertiesView-title">
Presentation
- <div className="propertiesView-title-icon" onPointerDown={this.props.onDown}>
- <FontAwesomeIcon icon="times" color="black" size="sm" />
- </div>
</div>
<div className="propertiesView-name">
{this.editableTitle}
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index 75fc8bf85..5832a2181 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -388,6 +388,12 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
onPointerDown={() => this.changeFollowBehavior("inTab")}>
Always open in new tab
</div>
+ {this.props.linkDoc.linksToAnnotation ?
+ <div className="linkEditor-followingDropdown-option"
+ onPointerDown={() => this.changeFollowBehavior("openExternal")}>
+ Always open in external page
+ </div>
+ : null}
</div>
</div>
</div>;
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index f4aed94e7..b95fccf2a 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -1,9 +1,9 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faArrowRight, faChevronDown, faChevronUp, faEdit, faEye, faTimes, faPencilAlt, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon, FontAwesomeIconProps } from '@fortawesome/react-fontawesome';
-import { action, observable } from 'mobx';
+import { action, observable, runInAction } from 'mobx';
import { observer } from "mobx-react";
-import { Doc, DocListCast } from '../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { Cast, StrCast } from '../../../fields/Types';
import { DragManager } from '../../util/DragManager';
import { LinkManager } from '../../util/LinkManager';
@@ -11,13 +11,16 @@ import { ContextMenu } from '../ContextMenu';
import './LinkMenuItem.scss';
import React = require("react");
import { DocumentManager } from '../../util/DocumentManager';
-import { setupMoveUpEvents, emptyFunction } from '../../../Utils';
+import { setupMoveUpEvents, emptyFunction, Utils, simulateMouseClick } from '../../../Utils';
import { DocumentView } from '../nodes/DocumentView';
import { DocumentLinksButton } from '../nodes/DocumentLinksButton';
import { LinkDocPreview } from '../nodes/LinkDocPreview';
+import { Hypothesis } from '../../util/HypothesisUtils';
+import { Id } from '../../../fields/FieldSymbols';
import { Tooltip } from '@material-ui/core';
import { DocumentType } from '../../documents/DocumentTypes';
import { undoBatch } from '../../util/UndoManager';
+import { WebField } from '../../../fields/URLField';
library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp, faPencilAlt, faEyeSlash);
@@ -148,23 +151,35 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
}
@action.bound
- async followDefault() {
+ followDefault() {
DocumentLinksButton.EditLink = undefined;
LinkDocPreview.LinkInfo = undefined;
+ const linkDoc = this.props.linkDoc;
- if (this.props.linkDoc.followLinkLocation && this.props.linkDoc.followLinkLocation !== "Default") {
- this.props.addDocTab(this.props.destinationDoc, StrCast(this.props.linkDoc.followLinkLocation));
+ if (linkDoc.followLinkLocation === "openExternal" && this.props.destinationDoc.type === DocumentType.WEB) {
+ window.open(`${StrCast(linkDoc.annotationUri)}#annotations:${StrCast(linkDoc.annotationId)}`, '_blank');
+ return;
+ }
+
+ if (linkDoc.followLinkLocation && linkDoc.followLinkLocation !== "Default") {
+ this.props.addDocTab(this.props.destinationDoc, StrCast(linkDoc.followLinkLocation));
} else {
DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.sourceDoc, doc => this.props.addDocTab(doc, "onRight"), false);
}
+
+ linkDoc.linksToAnnotation && Hypothesis.scrollToAnnotation(StrCast(this.props.linkDoc.annotationId), this.props.destinationDoc);
}
@undoBatch
@action
deleteLink = (): void => {
+ this.props.linkDoc.linksToAnnotation && Hypothesis.deleteLink(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc);
LinkManager.Instance.deleteLink(this.props.linkDoc);
- LinkDocPreview.LinkInfo = undefined;
- DocumentLinksButton.EditLink = undefined;
+
+ runInAction(() => {
+ LinkDocPreview.LinkInfo = undefined;
+ DocumentLinksButton.EditLink = undefined;
+ });
}
@undoBatch
@@ -233,7 +248,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
<FontAwesomeIcon className="destination-icon" icon={destinationIcon} size="sm" /></div>
<p className="linkMenu-destination-title"
onPointerDown={this.followDefault}>
- {title}
+ {this.props.linkDoc.linksToAnnotation && Cast(this.props.destinationDoc.data, WebField)?.url.href === this.props.linkDoc.annotationUri ? "Annotation in" : ""} {title}
</p>
</div>
{this.props.linkDoc.description !== "" ? <p className="linkMenu-description">
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index c2f27c85a..31dd33fc1 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -2,18 +2,23 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Tooltip } from "@material-ui/core";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc } from "../../../fields/Doc";
+import { Doc, DocListCast, Opt } from "../../../fields/Doc";
+import { DocumentType } from "../../documents/DocumentTypes";
+import { emptyFunction, setupMoveUpEvents, returnFalse, Utils, emptyPath } from "../../../Utils";
import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, returnFalse, setupMoveUpEvents, emptyPath } from "../../../Utils";
-import { DocUtils } from "../../documents/Documents";
+import { DocUtils, Docs } from "../../documents/Documents";
import { DragManager } from "../../util/DragManager";
import { LinkManager } from "../../util/LinkManager";
import { undoBatch, UndoManager } from "../../util/UndoManager";
-import './DocumentLinksButton.scss';
import { DocumentView } from "./DocumentView";
+import { StrCast, Cast } from "../../../fields/Types";
import { LinkDescriptionPopup } from "./LinkDescriptionPopup";
+import { Hypothesis } from "../../util/HypothesisUtils";
+import { Id } from "../../../fields/FieldSymbols";
import { TaskCompletionBox } from "./TaskCompletedBox";
import React = require("react");
+import './DocumentLinksButton.scss';
+
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -30,7 +35,12 @@ interface DocumentLinksButtonProps {
export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> {
private _linkButton = React.createRef<HTMLDivElement>();
- @observable public static StartLink: DocumentView | undefined;
+ @observable public static StartLink: Doc | undefined;
+ @observable public static AnnotationId: string | undefined;
+ @observable public static AnnotationUri: string | undefined;
+
+ @observable public static invisibleWebDoc: Opt<Doc>;
+ public static invisibleWebRef = React.createRef<HTMLDivElement>();
@action @undoBatch
onLinkButtonMoved = (e: PointerEvent) => {
@@ -67,10 +77,10 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => {
if (doubleTap && this.props.InMenu && this.props.StartLink) {
//action(() => Doc.BrushDoc(this.props.View.Document));
- if (DocumentLinksButton.StartLink === this.props.View) {
+ if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
DocumentLinksButton.StartLink = undefined;
} else {
- DocumentLinksButton.StartLink = this.props.View;
+ DocumentLinksButton.StartLink = this.props.View.props.Document;
}
} else if (!this.props.InMenu) {
DocumentLinksButton.EditLink = this.props.View;
@@ -81,10 +91,12 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
@action @undoBatch
onLinkClick = (e: React.MouseEvent): void => {
if (this.props.InMenu && this.props.StartLink) {
- if (DocumentLinksButton.StartLink === this.props.View) {
+ DocumentLinksButton.AnnotationId = undefined;
+ DocumentLinksButton.AnnotationUri = undefined;
+ if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
DocumentLinksButton.StartLink = undefined;
} else {
- DocumentLinksButton.StartLink = this.props.View;
+ DocumentLinksButton.StartLink = this.props.View.props.Document;
}
//action(() => Doc.BrushDoc(this.props.View.Document));
@@ -95,37 +107,64 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
completeLink = (e: React.PointerEvent): void => {
setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action((e, doubleTap) => {
- if (doubleTap && this.props.InMenu && !this.props.StartLink) {
- if (DocumentLinksButton.StartLink === this.props.View) {
+ if (doubleTap && !this.props.StartLink) {
+ if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
DocumentLinksButton.StartLink = undefined;
- } else if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View) {
- const linkDoc = DocUtils.MakeLink({ doc: DocumentLinksButton.StartLink.props.Document }, { doc: this.props.View.props.Document }, "long drag");
+ DocumentLinksButton.AnnotationId = undefined;
+ } else if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document) {
+ const sourceDoc = DocumentLinksButton.StartLink;
+ const targetDoc = this.props.View.props.Document;
+ const linkDoc = DocUtils.MakeLink({ doc: sourceDoc }, { doc: targetDoc }, "long drag");
+
LinkManager.currentLink = linkDoc;
- if (linkDoc) {
- TaskCompletionBox.textDisplayed = "Link Created";
- TaskCompletionBox.popupX = e.screenX;
- TaskCompletionBox.popupY = e.screenY - 133;
- TaskCompletionBox.taskCompleted = true;
-
- LinkDescriptionPopup.popupX = e.screenX;
- LinkDescriptionPopup.popupY = e.screenY - 100;
- LinkDescriptionPopup.descriptionPopup = true;
-
- setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500);
- }
+
+ runInAction(() => {
+ if (linkDoc) {
+ TaskCompletionBox.textDisplayed = "Link Created";
+ TaskCompletionBox.popupX = e.screenX;
+ TaskCompletionBox.popupY = e.screenY - 133;
+ TaskCompletionBox.taskCompleted = true;
+
+ LinkDescriptionPopup.popupX = e.screenX;
+ LinkDescriptionPopup.popupY = e.screenY - 100;
+ LinkDescriptionPopup.descriptionPopup = true;
+
+ LinkDescriptionPopup.popupX = e.screenX;
+ LinkDescriptionPopup.popupY = e.screenY - 100;
+ LinkDescriptionPopup.descriptionPopup = true;
+
+ setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500);
+ }
+ });
}
}
})));
}
- finishLinkClick = undoBatch(action((screenX: number, screenY: number) => {
- if (DocumentLinksButton.StartLink === this.props.View) {
+
+ public static finishLinkClick = undoBatch(action((screenX: number, screenY: number, startLink: Doc, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView,) => {
+ if (startLink === endLink) {
DocumentLinksButton.StartLink = undefined;
- } else if (this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View) {
- const linkDoc = DocUtils.MakeLink({ doc: DocumentLinksButton.StartLink.props.Document }, { doc: this.props.View.props.Document }, "long drag");
+ DocumentLinksButton.AnnotationId = undefined;
+ DocumentLinksButton.AnnotationUri = undefined;
+ //!this.props.StartLink
+ } else if (startLink !== endLink) {
+ const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "long drag");
// this notifies any of the subviews that a document is made so that they can make finer-grained hyperlinks (). see note above in onLInkButtonMoved
- DocumentLinksButton.StartLink._link = this.props.View._link = linkDoc;
- setTimeout(action(() => DocumentLinksButton.StartLink!._link = this.props.View._link = undefined), 0);
+ if (endLinkView) {
+ startLink._link = endLinkView._link = linkDoc;
+ setTimeout(action(() => startLink._link = endLinkView._link = undefined), 0);
+ }
LinkManager.currentLink = linkDoc;
+
+ if (DocumentLinksButton.AnnotationId && DocumentLinksButton.AnnotationUri) { // if linking from a Hypothes.is annotation
+ Doc.GetProto(linkDoc as Doc).linksToAnnotation = true;
+ Doc.GetProto(linkDoc as Doc).annotationId = DocumentLinksButton.AnnotationId;
+ Doc.GetProto(linkDoc as Doc).annotationUri = DocumentLinksButton.AnnotationUri;
+ const dashHyperlink = Utils.prepend("/doc/" + (startIsAnnotation ? endLink[Id] : startLink[Id]));
+ Hypothesis.makeLink(StrCast(startIsAnnotation ? endLink.title : startLink.title), dashHyperlink, DocumentLinksButton.AnnotationId,
+ (startIsAnnotation ? startLink : endLink)); // edit annotation to add a Dash hyperlink to the linked doc
+ }
+
if (linkDoc) {
TaskCompletionBox.textDisplayed = "Link Created";
TaskCompletionBox.popupX = screenX;
@@ -137,8 +176,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
LinkDescriptionPopup.popupY = screenY - 100;
LinkDescriptionPopup.descriptionPopup = true;
}
-
- setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500);
+ setTimeout(action(() => { TaskCompletionBox.taskCompleted = false; }), 2500);
}
}
}));
@@ -198,7 +236,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
link : links.length}
</div>
- {this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View ?
+ {this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ?
<div className={"documentLinksButton-endLink"}
style={{
width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px",
@@ -206,12 +244,15 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
border: DocumentLinksButton.StartLink ? "" : "none"
}}
onPointerDown={DocumentLinksButton.StartLink ? this.completeLink : emptyFunction}
- onClick={e => DocumentLinksButton.StartLink ? this.finishLinkClick(e.screenX, e.screenY) : emptyFunction} /> : (null)}
- {DocumentLinksButton.StartLink === this.props.View && this.props.InMenu && this.props.StartLink ? <div className={"documentLinksButton-startLink"}
- style={{ width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px" }}
- onPointerDown={this.clearLinks} onClick={this.clearLinks}
- /> : (null)}
- </div>;
+ onClick={e => DocumentLinksButton.StartLink ? DocumentLinksButton.finishLinkClick(e.screenX, e.screenY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View) : emptyFunction} /> : (null)
+ }
+ {
+ DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.InMenu && this.props.StartLink ? <div className={"documentLinksButton-startLink"}
+ style={{ width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px" }}
+ onPointerDown={this.clearLinks} onClick={this.clearLinks}
+ /> : (null)
+ }
+ </div >;
return (!links.length) && !this.props.AlwaysOn ? (null) :
this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink) ?
@@ -223,6 +264,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
</Tooltip> :
linkButton;
}
+
render() {
return this.linkButton;
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 444583af3..47e1b2715 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -565,12 +565,23 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
}
- @undoBatch
+ @undoBatch @action
deleteClicked = (): void => {
if (Doc.UserDoc().activeWorkspace === this.props.Document) {
alert("Can't delete the active workspace");
} else {
+ const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;
+ const selected = SelectionManager.SelectedDocuments().slice();
SelectionManager.DeselectAll();
+
+ selected.map(dv => {
+ const effectiveAcl = GetEffectiveAcl(dv.props.Document);
+ if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) { // deletes whatever you have the right to delete
+ recent && Doc.AddDocToList(recent, "data", dv.props.Document, undefined, true, true);
+ dv.props.removeDocument?.(dv.props.Document);
+ }
+ });
+
this.props.Document.deleted = true;
this.props.removeDocument?.(this.props.Document);
}
diff --git a/src/client/views/nodes/FontIconBox.scss b/src/client/views/nodes/FontIconBox.scss
index 9709e1dbd..6a540269e 100644
--- a/src/client/views/nodes/FontIconBox.scss
+++ b/src/client/views/nodes/FontIconBox.scss
@@ -60,7 +60,7 @@
.menuButton-icon-square {
width: auto;
- height: 32px;
+ height: 29px;
padding: 4px;
}
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index c0eb78d98..a6b1678b5 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -64,7 +64,10 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
const backgroundColor = StrCast(this.layoutDoc._backgroundColor, StrCast(this.rootDoc.backgroundColor, this.props.backgroundColor?.(this.rootDoc)));
const shape = StrCast(this.layoutDoc.iconShape, "round");
const button = <button className={`menuButton-${shape}`} ref={this._ref} onContextMenu={this.specificContextMenu}
- style={{ boxShadow: this.layoutDoc.ischecked ? `4px 4px 12px black` : undefined, backgroundColor: this.layoutDoc.iconShape === "square" ? backgroundColor : "" }}>
+ style={{
+ boxShadow: this.layoutDoc.ischecked ? `4px 4px 12px black` : undefined,
+ backgroundColor: this.layoutDoc.iconShape === "square" ? backgroundColor : "",
+ }}>
<div className="menuButton-wrap">
{<FontAwesomeIcon className={`menuButton-icon-${shape}`} icon={StrCast(this.dataDoc.icon, "user") as any} color={color}
size={this.layoutDoc.iconShape === "square" ? "sm" : "lg"} />}
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 502fd51f3..b7af4683e 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -34,7 +34,8 @@ const PresBoxDocument = makeInterface(documentSchema);
@observer
export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>(PresBoxDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); }
- static Instance: PresBox;
+
+ public static Instance: PresBox;
@observable _isChildActive = false;
@observable _moveOnFromAudio: boolean = true;