aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts6
-rw-r--r--src/client/util/DragManager.ts48
-rw-r--r--src/client/util/LinkManager.ts2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx102
-rw-r--r--src/client/views/linking/LinkEditor.scss88
-rw-r--r--src/client/views/linking/LinkEditor.tsx69
-rw-r--r--src/client/views/linking/LinkMenu.scss75
-rw-r--r--src/client/views/linking/LinkMenu.tsx26
-rw-r--r--src/client/views/linking/LinkMenuGroup.tsx2
-rw-r--r--src/client/views/linking/LinkMenuItem.scss69
-rw-r--r--src/client/views/linking/LinkMenuItem.tsx40
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx14
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx4
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.scss99
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx53
15 files changed, 494 insertions, 203 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index fa85d58f0..565e7c25d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -909,7 +909,7 @@ export namespace DocUtils {
DocUtils.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: doc }, { doc: d }, "audio link", "audio timeline"));
}
- export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", description: string = "", id?: string) {
+ export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", description: string = "", id?: string, linkedText?: string) {
const sv = DocumentManager.Instance.getDocumentView(source.doc);
if (sv && sv.props.ContainingCollectionDoc === target.doc) return;
if (target.doc === Doc.UserDoc()) return undefined;
@@ -921,6 +921,10 @@ export namespace DocUtils {
Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(self)");
Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(self)");
+ if (linkedText) {
+ Doc.GetProto(linkDoc).linkedText = linkedText;
+ }
+
return linkDoc;
}
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 2ceafff30..64c3d8458 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -427,6 +427,54 @@ export namespace DragManager {
}, dragData.droppedDocuments);
}
+ const target = document.elementFromPoint(e.x, e.y);
+
+ const complete = new DragCompleteEvent(false, dragData);
+
+ if (target) {
+ target.dispatchEvent(
+ new CustomEvent<React.DragEvent>("dashDragging", {
+ bubbles: true,
+ detail: {
+ shiftKey: e.shiftKey,
+ altKey: e.altKey,
+ metaKey: e.metaKey,
+ ctrlKey: e.ctrlKey,
+ clientX: e.clientX,
+ clientY: e.clientY,
+ dataTransfer: new DataTransfer,
+ button: e.button,
+ buttons: e.buttons,
+ getModifierState: e.getModifierState,
+ movementX: e.movementX,
+ movementY: e.movementY,
+ pageX: e.pageX,
+ pageY: e.pageY,
+ relatedTarget: e.relatedTarget,
+ screenX: e.screenX,
+ screenY: e.screenY,
+ detail: e.detail,
+ view: e.view ? e.view : new Window,
+ nativeEvent: new DragEvent("dashDragging"),
+ currentTarget: target,
+ target: target,
+ bubbles: true,
+ cancelable: true,
+ defaultPrevented: true,
+ eventPhase: e.eventPhase,
+ isTrusted: true,
+ preventDefault: e.preventDefault,
+ isDefaultPrevented: () => true,
+ stopPropagation: e.stopPropagation,
+ isPropagationStopped: () => true,
+ persist: emptyFunction,
+ timeStamp: e.timeStamp,
+ type: "dashDragging"
+ }
+ })
+ );
+ }
+
const { thisX, thisY } = snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom);
const moveX = thisX - lastX;
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 6da581f35..50f3fc1d6 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -3,6 +3,7 @@ import { List } from "../../fields/List";
import { listSpec } from "../../fields/Schema";
import { Cast, StrCast } from "../../fields/Types";
import { Scripting } from "./Scripting";
+import { undoBatch } from "./UndoManager";
/*
* link doc:
@@ -52,6 +53,7 @@ export class LinkManager {
return false;
}
+ @undoBatch
public deleteLink(linkDoc: Doc): boolean {
if (LinkManager.Instance.LinkManagerDoc && linkDoc instanceof Doc) {
Doc.RemoveDocFromList(LinkManager.Instance.LinkManagerDoc, "data", linkDoc);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d9aabd7c2..fdc8536f8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -101,6 +101,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@observable _clusterSets: (Doc[])[] = [];
@observable _timelineRef = React.createRef<Timeline>();
+ @observable _marqueeRef = React.createRef<HTMLDivElement>();
+ @observable canPanX: boolean = true;
+ @observable canPanY: boolean = true;
+
@computed get fitToContentScaling() { return this.fitToContent ? NumCast(this.layoutDoc.fitToContentScaling, 1) : 1; }
@computed get fitToContent() { return (this.props.fitToBox || this.Document._fitToBox) && !this.isAnnotationOverlay; }
@computed get parentScaling() { return this.props.ContentScaling && this.fitToContent && !this.isAnnotationOverlay ? this.props.ContentScaling() : 1; }
@@ -1143,10 +1147,19 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
this._layoutComputeReaction = reaction(() => this.doLayoutComputation,
(elements) => this._layoutElements = elements || [],
{ fireImmediately: true, name: "doLayout" });
+
+ const handler = (e: Event) => this.handleDragging(e, (e as CustomEvent<DragEvent>).detail);
+
+ document.addEventListener("dashDragging", handler);
}
+
componentWillUnmount() {
this._layoutComputeReaction?.();
+
+ const handler = (e: Event) => this.handleDragging(e, (e as CustomEvent<DragEvent>).detail);
+ document.removeEventListener("dashDragging", handler);
}
+
@computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); }
elementFunc = () => this._layoutElements;
@@ -1155,6 +1168,75 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
}
+
+ // <div ref={this._marqueeRef}>
+
+ @action
+ handleDragging = (e: CustomEvent<React.DragEvent>, de: DragEvent) => {
+
+ const top = this.panX();
+ const left = this.panY();
+
+ const size = this.getTransform().transformDirection(this.props.PanelWidth(), this.props.PanelHeight());
+ const scale = this.getLocalTransform().inverse().Scale;
+
+ if (this._marqueeRef?.current) {
+ const dragX = e.detail.clientX;
+ const dragY = e.detail.clientY;
+ const bounds = this._marqueeRef.current?.getBoundingClientRect()!;
+
+ if (dragX - bounds.left < 25) {
+ console.log("PAN left ");
+
+ // if (this.canPanX) {
+ // this.Document._panX = left - 5;
+ // setTimeout(action(() => {
+ // this.canPanX = true;
+ // this.panX();
+ // }), 250);
+ // this.canPanX = false;
+ // }
+ } else if (bounds.right - dragX < 25) {
+ console.log("PAN right ");
+
+ // if (this.canPanX) {
+ // this.Document._panX = left + 5;
+ // setTimeout(action(() => {
+ // this.panX();
+ // this.canPanX = true;
+ // }), 250);
+ // this.canPanX = false;
+ // }
+
+ }
+
+ if (dragY - bounds.top < 25) {
+ console.log("PAN top ");
+
+ // if (this.canPanY) {
+ // this.Document._panY = top - 5;
+ // setTimeout(action(() => {
+ // this.canPanY = true;
+ // this.panY();
+ // }), 250);
+ // this.canPanY = false;
+ // }
+ } else if (bounds.bottom - dragY < 25) {
+ console.log("PAN bottom ");
+
+ // if (this.canPanY) {
+ // this.Document._panY = top + 5;
+ // setTimeout(action(() => {
+ // this.panY();
+ // this.canPanY = true;
+ // }), 250);
+ // this.canPanY = false;
+ // }
+
+ }
+ }
+ }
+
promoteCollection = undoBatch(action(() => {
const childDocs = this.childDocs.slice();
childDocs.forEach(doc => {
@@ -1336,7 +1418,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
return false;
});
@computed get marqueeView() {
- return <MarqueeView {...this.props}
+ return <MarqueeView
+ {...this.props}
nudge={this.nudge}
addDocTab={this.addDocTab}
activeDocuments={this.getActiveDocuments}
@@ -1346,14 +1429,15 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
getContainerTransform={this.getContainerTransform}
getTransform={this.getTransform}
isAnnotationOverlay={this.isAnnotationOverlay}>
- <CollectionFreeFormViewPannableContents
- centeringShiftX={this.centeringShiftX}
- centeringShiftY={this.centeringShiftY}
- transition={Cast(this.layoutDoc._viewTransition, "string", null)}
- viewDefDivClick={this.props.viewDefDivClick}
- zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
- {this.children}
- </CollectionFreeFormViewPannableContents>
+ <div ref={this._marqueeRef}>
+ <CollectionFreeFormViewPannableContents
+ centeringShiftX={this.centeringShiftX}
+ centeringShiftY={this.centeringShiftY}
+ transition={Cast(this.layoutDoc._viewTransition, "string", null)}
+ viewDefDivClick={this.props.viewDefDivClick}
+ zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
+ {this.children}
+ </CollectionFreeFormViewPannableContents></div>
{this.showTimeline ? <Timeline ref={this._timelineRef} {...this.props} /> : (null)}
</MarqueeView>;
}
diff --git a/src/client/views/linking/LinkEditor.scss b/src/client/views/linking/LinkEditor.scss
index 406a38c26..87afc99eb 100644
--- a/src/client/views/linking/LinkEditor.scss
+++ b/src/client/views/linking/LinkEditor.scss
@@ -3,12 +3,12 @@
.linkEditor {
width: 100%;
height: auto;
- font-size: 12px; // TODO
+ font-size: 13px; // TODO
user-select: none;
}
.linkEditor-button-back {
- margin-bottom: 6px;
+ //margin-bottom: 6px;
border-radius: 10px;
width: 18px;
height: 18px;
@@ -17,12 +17,13 @@
.linkEditor-info {
//border-bottom: 0.5px solid $light-color-secondary;
- padding-bottom: 4px;
+ //padding-bottom: 1px;
padding-top: 5px;
padding-left: 5px;
//margin-bottom: 6px;
display: flex;
justify-content: space-between;
+ color: black;
.linkEditor-linkedTo {
width: calc(100% - 26px);
@@ -31,30 +32,69 @@
}
}
+.linkEditor-moreInfo {
+ margin-left: 12px;
+ padding-left: 13px;
+ padding-right: 6.5px;
+ padding-bottom: 4px;
+ font-size: 9px;
+ //font-style: italic;
+ text-decoration-color: grey;
+
+ .button {
+ color: black;
+ }
+}
+
.linkEditor-description {
padding-left: 6.5px;
padding-right: 6.5px;
padding-bottom: 3.5px;
- .linkEditor-description-text {
- text-decoration-color: grey;
+ .linkEditor-description-label {
+ text-decoration-color: black;
+ color: black;
}
.linkEditor-description-input {
- border: 1px solid grey;
- border-radius: 4px;
- background-color: rgb(236, 236, 236);
- padding-left: 2px;
- padding-right: 2px;
- color: grey;
- text-decoration-color: grey;
+ display: flex;
+
+ .linkEditor-description-editing {
+ min-width: 85%;
+ //border: 1px solid grey;
+ //border-radius: 4px;
+ padding-left: 2px;
+ padding-right: 2px;
+ //margin-right: 4px;
+ color: black;
+ text-decoration-color: grey;
+ }
+
+ .linkEditor-description-add-button {
+ display: inline;
+ /* float: right; */
+ border-radius: 7px;
+ font-size: 9px;
+ background-color: black;
+ /* padding: 3px; */
+ padding-top: 4px;
+ padding-left: 7px;
+ padding-bottom: 4px;
+ padding-right: 8px;
+ height: 80%;
+ color: white;
+ }
}
}
.linkEditor-followingDropdown {
padding-left: 6.5px;
padding-right: 6.5px;
- padding-bottom: 3.5px;
+ padding-bottom: 6px;
+
+ .linkEditor-followingDropdown-label {
+ color: black;
+ }
.linkEditor-followingDropdown-dropdown {
@@ -62,14 +102,15 @@
border: 1px solid grey;
border-radius: 4px;
- background-color: rgb(236, 236, 236);
+ //background-color: rgb(236, 236, 236);
padding-left: 2px;
padding-right: 2px;
- color: grey;
- text-decoration-color: grey;
+ text-decoration-color: black;
+ color: rgb(94, 94, 94);
.linkEditor-followingDropdown-icon {
float: right;
+ color: black;
}
}
@@ -77,17 +118,22 @@
padding-left: 3px;
padding-right: 3px;
+ &:last-child {
+ border-bottom: none;
+ }
+
.linkEditor-followingDropdown-option {
- border: 0.25px dotted grey;
- background-color: rgb(236, 236, 236);
+ border: 0.25px solid grey;
+ //background-color: rgb(236, 236, 236);
padding-left: 2px;
padding-right: 2px;
color: grey;
text-decoration-color: grey;
font-size: 9px;
+ border-top: none;
&:hover {
- background-color: rgb(211, 210, 210);
+ background-color: rgb(187, 220, 231);
}
}
@@ -98,6 +144,10 @@
}
+
+
+
+
.linkEditor-button,
.linkEditor-addbutton {
width: 18px;
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index 014d57ed0..a26685318 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -1,10 +1,10 @@
import { library } from "@fortawesome/fontawesome-svg-core";
import { faArrowLeft, faCog, faEllipsisV, faExchangeAlt, faPlus, faTable, faTimes, faTrash } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable, computed } from "mobx";
+import { action, observable, computed, toJS } from "mobx";
import { observer } from "mobx-react";
import { Doc, Opt } from "../../../fields/Doc";
-import { StrCast } from "../../../fields/Types";
+import { StrCast, DateCast } from "../../../fields/Types";
import { Utils } from "../../../Utils";
import { LinkManager } from "../../util/LinkManager";
import './LinkEditor.scss';
@@ -291,6 +291,10 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
@observable followBehavior = this.props.linkDoc.follow ? this.props.linkDoc.follow : "Default";
+ @observable showInfo: boolean = false;
+
+ @computed get infoIcon() { if (this.showInfo) { return "chevron-up"; } return "chevron-down"; }
+
//@observable description = this.props.linkDoc.description ? StrCast(this.props.linkDoc.description) : "DESCRIPTION";
@@ -308,19 +312,45 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
}
}
+ @action
+ onKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
+ if (e.key === "Enter") {
+ this.setDescripValue(this.description);
+ document.getElementById('input')?.blur();
+ }
+ }
+
+ @action
+ onDown = () => {
+ this.setDescripValue(this.description);
+ }
+
+ @action
+ handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this.description = e.target.value;
+ }
+
+
@computed
get editDescription() {
return <div className="linkEditor-description">
<div className="linkEditor-description-label">
- Link Description:</div>
+ Link Label:</div>
<div className="linkEditor-description-input">
- <EditableView
- GetValue={() => StrCast(LinkManager.currentLink?.description)}
- SetValue={value => { this.setDescripValue(value); return false; }}
- contents={LinkManager.currentLink?.description}
- placeholder={"(optional) enter link description"}
- color={"rgb(88, 88, 88)"}
- ></EditableView></div></div>;
+ <div className="linkEditor-description-editing">
+ <input
+ style={{ width: "100%" }}
+ id="input"
+ value={this.description}
+ placeholder={"enter link label"}
+ // color={"rgb(88, 88, 88)"}
+ onKeyDown={this.onKey}
+ onChange={this.handleChange}
+ ></input>
+ </div>
+ <div className="linkEditor-description-add-button"
+ onPointerDown={this.onDown}>Add</div>
+ </div></div>;
}
@action
@@ -346,7 +376,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
{this.followBehavior}
<FontAwesomeIcon className="linkEditor-followingDropdown-icon"
icon={this.openDropdown ? "chevron-up" : "chevron-down"}
- size={"lg"} onPointerDown={this.changeDropdown} />
+ size={"lg"} />
</div>
<div className="linkEditor-followingDropdown-optionsList"
style={{ display: this.openDropdown ? "" : "none" }}>
@@ -367,6 +397,11 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
</div>;
}
+ @action
+ changeInfo = () => {
+ this.showInfo = !this.showInfo;
+ }
+
render() {
const destination = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
@@ -382,11 +417,17 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
style={{ display: this.props.hideback ? "none" : "" }}
onClick={this.props.showLinks}>
<FontAwesomeIcon icon="arrow-left" size="sm" /> </button>
- <p className="linkEditor-linkedTo">editing link to: <b>{
+ <p className="linkEditor-linkedTo">Editing Link to: <b>{
destination.proto?.title ?? destination.title ?? "untitled"}</b></p>
- <button className="linkEditor-button" onPointerDown={() => this.deleteLink()} title="Delete link">
- <FontAwesomeIcon icon="trash" size="sm" /></button>
+ {/* <button className="linkEditor-button" onPointerDown={() => this.deleteLink()} title="Delete link">
+ <FontAwesomeIcon icon="trash" size="sm" /></button> */}
+ <FontAwesomeIcon className="button" icon={this.infoIcon} size="lg" onPointerDown={this.changeInfo} />
</div>
+ {this.showInfo ? <div className="linkEditor-moreInfo">
+ <div>{this.props.linkDoc.author ? <div> <b>Author:</b> {this.props.linkDoc.author}</div> : null}</div>
+ <div>{this.props.linkDoc.creationDate ? <div> <b>Creation Date:</b>
+ {DateCast(this.props.linkDoc.creationDate).toString()}</div> : null}</div>
+ </div> : null}
<div>{this.editDescription}</div>
<div>{this.followingDropdown}</div>
diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss
index 4b1a3f425..4f7ac3275 100644
--- a/src/client/views/linking/LinkMenu.scss
+++ b/src/client/views/linking/LinkMenu.scss
@@ -1,33 +1,64 @@
@import "../globalCssVariables";
.linkMenu {
- width: 100%;
+ width: auto;
height: auto;
- //border: 1px solid black;
- &:hover {
- width: calc(auto + 26px);
+
+ .linkMenu-list {
+
+ display: inline-block;
+
+ border: 1px solid black;
+
+ box-shadow: 3px 3px 1.5px grey;
+
+ max-height: 170px;
+ overflow-y: scroll;
+ position: relative;
+ z-index: 10;
+ background: white;
+ min-width: 170px;
+ //border-radius: 5px;
+ //padding-top: 6.5px;
+ //padding-bottom: 6.5px;
+ //padding-left: 6.5px;
+ //padding-right: 2px;
+ //width: calc(auto + 50px);
+
+ white-space: nowrap;
+
+ overflow-x: hidden;
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ &:hover {
+ padding-right: 65px;
+ display: inline-block;
+ }
}
-}
-.linkMenu-list {
- border: 1px solid black;
- max-height: 200px;
- overflow-y: scroll;
- position: absolute;
- z-index: 10;
- background: white;
- min-width: 150px;
- border-radius: 5px;
- padding-top: 6.5px;
- padding-bottom: 6.5px;
- padding-left: 6.5px;
- padding-right: 2px;
- //width: calc(auto + 50px);
+ .linkMenu-listEditor {
+
+ display: inline-block;
+
+ border: 1px solid black;
+
+ box-shadow: 3px 3px 1.5px grey;
+
+ max-height: 170px;
+ overflow-y: scroll;
+ position: relative;
+ z-index: 10;
+ background: white;
+ min-width: 170px;
+ }
}
.linkMenu-group {
- //border-bottom: 0.5px solid lightgray;
+ border-bottom: 0.5px solid lightgray;
//@extend: 5px 0;
@@ -46,7 +77,7 @@
}
p.expand-one {
- width: calc(100% + 26px);
+ width: calc(100% + 20px);
}
.linkEditor-tableButton {
@@ -56,7 +87,7 @@
p {
width: 100%;
- padding: 4px 6px;
+ //padding: 4px 6px;
line-height: 12px;
border-radius: 5px;
font-weight: bold;
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 8a7b12f48..234cd5e07 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -1,4 +1,4 @@
-import { action, observable } from "mobx";
+import { action, observable, computed } from "mobx";
import { observer } from "mobx-react";
import { DocumentView } from "../nodes/DocumentView";
import { LinkEditor } from "./LinkEditor";
@@ -29,6 +29,15 @@ export class LinkMenu extends React.Component<Props> {
@observable private _linkMenuRef = React.createRef<HTMLDivElement>();
private _editorRef = React.createRef<HTMLDivElement>();
+ //@observable private _numLinks: number = 0;
+
+ // @computed get overflow() {
+ // if (this._numLinks) {
+ // return "scroll";
+ // }
+ // return "auto";
+ // }
+
@action
onClick = (e: PointerEvent) => {
@@ -81,13 +90,16 @@ export class LinkMenu extends React.Component<Props> {
const sourceDoc = this.props.docView.props.Document;
const groups: Map<string, Doc[]> = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc);
return <div className="linkMenu" ref={this._linkMenuRef} >
- <div className="linkMenu-list"
- style={{ left: this.props.location[0], top: this.props.location[1] }}>
- {!this._editingLink ?
- this.renderAllGroups(groups) :
+ {!this._editingLink ? <div className="linkMenu-list" style={{
+ left: this.props.location[0], top: this.props.location[1] }}>
+ {this.renderAllGroups(groups)}
+ </div> : <div className="linkMenu-listEditor" style={{
+ left: this.props.location[0], top: this.props.location[1]}}>
<LinkEditor sourceDoc={this.props.docView.props.Document} linkDoc={this._editingLink}
showLinks={action(() => this._editingLink = undefined)} />
- }
- </div> </div>;
+ </div>
+ }
+
+ </div>;
}
} \ No newline at end of file
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index ec17776e3..2f6b75437 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -82,11 +82,13 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
return (
<div className="linkMenu-group" ref={this._menuRef}>
+
{/* <div className="linkMenu-group-name">
<p ref={this._drag} onPointerDown={this.onLinkButtonDown}
className={this.props.groupType === "*" || this.props.groupType === "" ? "" : "expand-one"} > {this.props.groupType}:</p>
{this.props.groupType === "*" || this.props.groupType === "" ? <></> : this.viewGroupAsTable(this.props.groupType)}
</div> */}
+
<div className="linkMenu-group-wrapper">
{groupItems}
</div>
diff --git a/src/client/views/linking/LinkMenuItem.scss b/src/client/views/linking/LinkMenuItem.scss
index 67bf71fb9..e444b832b 100644
--- a/src/client/views/linking/LinkMenuItem.scss
+++ b/src/client/views/linking/LinkMenuItem.scss
@@ -4,6 +4,13 @@
// border-top: 0.5px solid $main-accent;
position: relative;
display: flex;
+ border-bottom: 0.5px solid black;
+
+ padding-left: 6.5px;
+ padding-right: 2px;
+
+ padding-top: 6.5px;
+ padding-bottom: 6.5px;
.linkMenu-name {
@@ -14,11 +21,35 @@
padding: 4px 2px;
//display: inline;
- .linkMenu-destination-title {
+ .linkMenu-source-title {
text-decoration: none;
- color: rgb(85, 120, 196);
- font-size: 14px;
- padding-bottom: 2px;
+ color: rgb(43, 43, 43);
+ font-size: 7px;
+ padding-bottom: 0.75px;
+ }
+
+
+ .linkMenu-title-wrapper {
+
+ display: flex;
+
+ .destination-icon {
+ float: left;
+ width: 12px;
+ height: 12px;
+ padding-right: 3px;
+ margin-right: 3px;
+ }
+
+ .linkMenu-destination-title {
+ text-decoration: none;
+ color: rgb(85, 120, 196);
+ font-size: 14px;
+ padding-bottom: 2px;
+ padding-right: 4px;
+ margin-right: 4px;
+ float: right;
+ }
}
.linkMenu-description {
@@ -29,12 +60,16 @@
}
p {
- //padding: 4px 2px;
+ padding-right: 4px;
line-height: 12px;
border-radius: 5px;
- overflow-wrap: break-word;
+ //overflow-wrap: break-word;
user-select: none;
}
+
+ &:hover {
+ padding-right: 8px;
+ }
}
}
@@ -53,6 +88,7 @@
&:hover {
+ width: calc(100% + 58px);
.linkMenu-item-buttons {
display: flex;
@@ -67,19 +103,13 @@
text-overflow: break;
}
- &.expand-two p {
- width: calc(100% - 52px);
- //text-decoration: underline;
- //color: rgb(15, 57, 148);
- //background-color: lightgray;
- }
+ // &.expand-two p {
+ // width: calc(100% - 63px);
+ // }
- &.expand-three p {
- width: calc(100% - 84px);
- //text-decoration: underline;
- //color: rgb(15, 57, 148);
- //background-color: lightgray;
- }
+ // &.expand-three p {
+ // width: calc(100% - 93px);
+ // }
}
}
}
@@ -96,7 +126,8 @@
width: 20px;
height: 20px;
margin: 0;
- margin-right: 6px;
+ margin-right: 4px;
+ padding-right: 6px;
border-radius: 50%;
pointer-events: auto;
background-color: $dark-color;
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 6af474513..d2363c7d3 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -1,5 +1,5 @@
import { library } from '@fortawesome/fontawesome-svg-core';
-import { faArrowRight, faChevronDown, faChevronUp, faEdit, faEye, faTimes, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
+import { faArrowRight, faChevronDown, faChevronUp, faEdit, faEye, faTimes, faPencilAlt, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, observable } from 'mobx';
import { observer } from "mobx-react";
@@ -15,7 +15,7 @@ import { setupMoveUpEvents, emptyFunction } from '../../../Utils';
import { DocumentView } from '../nodes/DocumentView';
import { DocumentLinksButton } from '../nodes/DocumentLinksButton';
import { LinkDocPreview } from '../nodes/LinkDocPreview';
-library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp, faPencilAlt);
+library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp, faPencilAlt, faEyeSlash);
interface LinkMenuItemProps {
@@ -78,7 +78,10 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
@action toggleShowMore(e: React.PointerEvent) { e.stopPropagation(); this._showMore = !this._showMore; }
onEdit = (e: React.PointerEvent): void => {
+
+ console.log("Edit");
LinkManager.currentLink = this.props.linkDoc;
+ console.log(this.props.linkDoc);
setupMoveUpEvents(this, e, this.editMoved, emptyFunction, () => this.props.showEditor(this.props.linkDoc));
}
@@ -176,9 +179,28 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
const keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType);
const canExpand = keys ? keys.length > 0 : false;
+ const eyeIcon = this.props.linkDoc.shown ? "eye-slash" : "eye";
+
+ const destinationIcon = this.props.destinationDoc.type === "image" ? "image" :
+ this.props.destinationDoc.type === "comparison" ? "columns" :
+ this.props.destinationDoc.type === "rtf" ? "font" :
+ this.props.destinationDoc.type === "collection" ? "folder" :
+ this.props.destinationDoc.type === "web" ? "globe-asia" :
+ this.props.destinationDoc.type === "screenshot" ? "photo-video" :
+ this.props.destinationDoc.type === "webcam" ? "video" :
+ this.props.destinationDoc.type === "audio" ? "microphone" :
+ this.props.destinationDoc.type === "button" ? "lightning" :
+ this.props.destinationDoc.type === "presentation" ? "tv" :
+ this.props.destinationDoc.type === "query" ? "search" :
+ this.props.destinationDoc.type === "script" ? "terminal" :
+ this.props.destinationDoc.type === "import" ? "cloud-upload-alt" :
+ this.props.destinationDoc.type === "docholder" ? "expand" : "question";
+
+
return (
<div className="linkMenu-item">
<div className={canExpand ? "linkMenu-item-content expand-three" : "linkMenu-item-content expand-two"}>
+
<div ref={this._drag} className="linkMenu-name" //title="drag to view target. click to customize."
onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
onPointerEnter={action(e => this.props.linkDoc && (LinkDocPreview.LinkInfo = {
@@ -190,9 +212,14 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
onPointerDown={this.onLinkButtonDown}>
<div className="linkMenu-text">
- <p className="linkMenu-destination-title"
- onPointerDown={this.followDefault}>
- {StrCast(this.props.destinationDoc.title)}</p>
+ {this.props.linkDoc.linkedText ? <p className="linkMenu-source-title">
+ Source: <b>{StrCast(this.props.linkDoc.linkedText)}</b></p> : null}
+ <div className="linkMenu-title-wrapper">
+ <FontAwesomeIcon className="destination-icon" icon={destinationIcon} size="sm" />
+ <p className="linkMenu-destination-title"
+ onPointerDown={this.followDefault}>
+ {StrCast(this.props.destinationDoc.title)}</p>
+ </div>
{this.props.linkDoc.description !== "" ? <p className="linkMenu-description">
{StrCast(this.props.linkDoc.description)}</p> : null} </div>
@@ -200,6 +227,9 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
{canExpand ? <div title="Show more" className="button" onPointerDown={e => this.toggleShowMore(e)}>
<FontAwesomeIcon className="fa-icon" icon={this._showMore ? "chevron-up" : "chevron-down"} size="sm" /></div> : <></>}
+ <div title="Show link" className="button" ref={this._editRef} onPointerDown={emptyFunction}>
+ <FontAwesomeIcon className="fa-icon" icon={eyeIcon} size="sm" /></div>
+
<div title="Edit link" className="button" ref={this._editRef} onPointerDown={this.onEdit}>
<FontAwesomeIcon className="fa-icon" icon="edit" size="sm" /></div>
<div title="Delete link" className="button" onPointerDown={this.deleteLink}>
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 7fb447cab..1af3318cb 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
import { Doc, DocListCast } from "../../../fields/Doc";
import { emptyFunction, setupMoveUpEvents, returnFalse } from "../../../Utils";
import { DragManager } from "../../util/DragManager";
-import { UndoManager } from "../../util/UndoManager";
+import { UndoManager, undoBatch } from "../../util/UndoManager";
import './DocumentLinksButton.scss';
import { DocumentView } from "./DocumentView";
import React = require("react");
@@ -29,7 +29,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
@observable public static StartLink: DocumentView | undefined;
- @action
+ @action @undoBatch
onLinkButtonMoved = (e: PointerEvent) => {
if (this._linkButton.current !== null) {
const linkDrag = UndoManager.StartBatch("Drag Link");
@@ -56,7 +56,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
return false;
}
-
+ @undoBatch
onLinkButtonDown = (e: React.PointerEvent): void => {
setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => {
if (doubleTap && this.props.InMenu) {
@@ -69,7 +69,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
}));
}
- @action
+ @action @undoBatch
onLinkClick = (e: React.MouseEvent): void => {
if (this.props.InMenu) {
DocumentLinksButton.StartLink = this.props.View;
@@ -80,7 +80,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
}
}
- @action
+ @action @undoBatch
completeLink = (e: React.PointerEvent): void => {
setupMoveUpEvents(this, e, returnFalse, emptyFunction, action((e, doubleTap) => {
if (doubleTap) {
@@ -111,7 +111,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
}));
}
- @action
+ @action @undoBatch
finishLinkClick = (e: React.MouseEvent) => {
if (DocumentLinksButton.StartLink === this.props.View) {
DocumentLinksButton.StartLink = undefined;
@@ -152,7 +152,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
return (!links.length || links[0].hidden) && !this.props.AlwaysOn ? (null) :
<div title={title} ref={this._linkButton} style={{ minWidth: 20, minHeight: 20, position: "absolute", left: this.props.Offset?.[0] }}>
<div className={"documentLinksButton"} style={{
- backgroundColor: DocumentLinksButton.StartLink ? "transparent" : this.props.InMenu ? "black" : "",
+ backgroundColor: DocumentLinksButton.StartLink && !!!this.props.InMenu ? "transparent" : this.props.InMenu ? "black" : "",
color: this.props.InMenu ? "white" : "black",
width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px", fontWeight: "bold"
}}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 962d0012f..bff6f1c8c 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -174,6 +174,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
linkOnDeselect: Map<string, string> = new Map();
doLinkOnDeselect() {
+
+ console.log("link on deselect");
Array.from(this.linkOnDeselect.entries()).map(entry => {
const key = entry[0];
const value = entry[1];
@@ -944,6 +946,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
frag.forEach(node => nodes.push(marker(node)));
return Fragment.fromArray(nodes);
}
+
+
function addLinkMark(node: Node, title: string, linkId: string) {
if (!node.isText) {
const content = addMarkToFrag(node.content, (node: Node) => addLinkMark(node, title, linkId));
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss b/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss
index 9089e7039..286ccf22d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss
@@ -8,6 +8,45 @@
margin-bottom: 7px;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
+ box-shadow: 3px 3px 1.5px grey;
+
+ .FormattedTextBoxComment-title {
+ background-color: white;
+ border: 8px solid white;
+
+ .FormattedTextBoxComment-button {
+ display: inline;
+ padding-left: 6px;
+ padding-right: 6px;
+ padding-top: 2.5px;
+ padding-bottom: 2.5px;
+ width: 20px;
+ height: 20px;
+ margin: 0;
+ margin-right: 6px;
+ border-radius: 50%;
+ pointer-events: auto;
+ background-color: rgb(0, 0, 0);
+ color: rgb(255, 255, 255);
+ transition: transform 0.2s;
+ text-align: center;
+ position: relative;
+ font: 10px;
+
+ &:hover {
+ background-color: rgb(77, 77, 77);
+ cursor: grab;
+ }
+ }
+
+ .FormattedTextBoxComment-preview-wrapper {
+ max-width: 180px;
+ max-height: 168px;
+ overflow: hidden;
+ overflow-y: hidden;
+ padding-top: 5px;
+ }
+ }
}
.FormattedTextBox-tooltip:before {
@@ -42,64 +81,4 @@
top: 50%;
right: 0;
transform: translateY(-50%);
-
- .FormattedTextBoxComment-button {
- width: 20px;
- height: 20px;
- margin: 0;
- margin-right: 6px;
- border-radius: 50%;
- pointer-events: auto;
- background-color: rgb(38, 40, 41);
- color: rgb(178, 181, 184);
- font-size: 65%;
- transition: transform 0.2s;
- text-align: center;
- position: relative;
-
- // margin-top: "auto";
- // margin-bottom: "auto";
- // background: black;
- // color: white;
- // display: inline-block;
- // border-radius: 18px;
- // font-size: 12.5px;
- // width: 18px;
- // height: 18px;
- // margin-top: auto;
- // margin-bottom: auto;
- // margin-right: 3px;
- // cursor: pointer;
- // transition: transform 0.2s;
-
- .FormattedTextBoxComment-fa-icon {
- margin-top: "auto";
- margin-bottom: "auto";
- background: black;
- color: white;
- display: inline-block;
- border-radius: 18px;
- font-size: 12.5px;
- width: 18px;
- height: 18px;
- margin-top: auto;
- margin-bottom: auto;
- margin-right: 3px;
- cursor: pointer;
- transition: transform 0.2s;
- // position: absolute;
- // top: 50%;
- // left: 50%;
- // transform: translate(-50%, -50%);
- }
-
- &:last-child {
- margin-right: 0;
- }
-
- &:hover {
- background: rgb(53, 146, 199);
- ;
- }
- }
} \ No newline at end of file
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 56826e5c7..79b09b374 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -241,52 +241,22 @@ export class FormattedTextBoxComment {
}
if (target?.author) {
FormattedTextBoxComment.showCommentbox("", view, nbef);
- const docPreview = <div style={{ backgroundColor: "white", border: "8px solid white" }}>
+
+
+ const docPreview = <div className="FormattedTextBoxComment-title">
{target.title}
<div className="wrapper" style={{ float: "right" }}>
- <div title="Delete link" className="FormattedTextBoxComment-button" style={{
- display: "inline",
- paddingLeft: "6px",
- paddingRight: "6px",
- paddingTop: "2.5px",
- paddingBottom: "2.5px",
- width: "20px",
- height: "20px",
- margin: 0,
- marginRight: "6px",
- borderRadius: "50%",
- pointerEvents: "auto",
- backgroundColor: "rgb(38, 40, 41)",
- color: "rgb(178, 181, 184)",
- transition: "transform 0.2s",
- textAlign: "center",
- position: "relative"
- }} ref={(r) => this._deleteRef = r}>
- <FontAwesomeIcon className="FormattedTextBox-fa-icon" icon="trash"
+ <div title="Delete link" className="FormattedTextBoxComment-button"
+ ref={(r) => this._deleteRef = r}>
+ <FontAwesomeIcon className="FormattedTextBoxComment-fa-icon" icon="trash" color="white"
size="sm" /></div>
- <div title="Follow link" className="FormattedTextBoxComment-button" style={{
- display: "inline",
- paddingLeft: "6px",
- paddingRight: "6px",
- paddingTop: "2.5px",
- paddingBottom: "2.5px",
- width: "20px",
- height: "20px",
- margin: 0,
- marginRight: "6px",
- borderRadius: "50%",
- pointerEvents: "auto",
- backgroundColor: "rgb(38, 40, 41)",
- color: "rgb(178, 181, 184)",
- transition: "transform 0.2s",
- textAlign: "center",
- position: "relative"
- }} ref={(r) => this._followRef = r}>
- <FontAwesomeIcon className="FormattedTextBox-fa-icon" icon="arrow-right"
+ <div title="Follow link" className="FormattedTextBoxComment-button"
+ ref={(r) => this._followRef = r}>
+ <FontAwesomeIcon className="FormattedTextBoxComment-fa-icon" icon="arrow-right" color="white"
size="sm" />
</div>
</div>
- <div className="wrapper" style={{
+ <div className="FormattedTextBoxComment-preview-wrapper" style={{
maxWidth: "180px", maxHeight: "168px", overflow: "hidden",
overflowY: "hidden", paddingTop: "5px"
}}>
@@ -318,6 +288,9 @@ export class FormattedTextBoxComment {
/>
</div>
</div>;
+
+
+
FormattedTextBoxComment.showCommentbox("", view, nbef);
ReactDOM.render(docPreview, FormattedTextBoxComment.tooltipText);