aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/util/SelectionManager.ts4
-rw-r--r--src/client/views/PropertiesView.scss42
-rw-r--r--src/client/views/PropertiesView.tsx236
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx53
-rw-r--r--src/fields/documentSchemas.ts1
5 files changed, 318 insertions, 18 deletions
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 2cba2c1f2..6674f684d 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -4,6 +4,7 @@ import { Doc, Opt } from "../../fields/Doc";
import { DocumentType } from "../documents/DocumentTypes";
import { CollectionViewType } from "../views/collections/CollectionView";
import { DocumentView } from "../views/nodes/DocumentView";
+import { CurrentUserUtils } from "./CurrentUserUtils";
export namespace SelectionManager {
@@ -43,6 +44,9 @@ export namespace SelectionManager {
}
@action
DeselectAll(): void {
+ if (CurrentUserUtils.propertiesWidth > 0) {
+ CurrentUserUtils.propertiesWidth = 0;
+ }
manager.SelectedSchemaDocument = undefined;
Array.from(manager.SelectedViews.keys()).map(dv => dv.props.whenChildContentsActiveChanged(false));
manager.SelectedViews.clear();
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index 554f137cb..437df4739 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -2,6 +2,7 @@
.propertiesView {
height: 100%;
+ width: 250;
font-family: "Roboto";
cursor: auto;
@@ -836,3 +837,44 @@
.properties-flyout {
grid-column: 2/4;
}
+
+.propertiesView-linking {
+ padding: 5%;
+}
+
+.propertiesView-section {
+ padding: 10px 0;
+}
+
+.propertiesView-input {
+ padding: 4px 8px;
+
+ .text {
+ width: 100%;
+ }
+
+ &.first {
+ padding-top: 6px;
+ }
+
+ &.inline {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ }
+
+ .propertiesButton {
+ width: 4rem;
+ }
+}
+
+.propertiesView-label {
+ font-weight: bold;
+ font-size: 12.5px;
+ padding: 4px;
+ display: flex;
+ color: white;
+ padding-left: 8px;
+ background-color: rgb(51, 51, 51);
+} \ No newline at end of file
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 8e2426006..92e6cbb89 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1,18 +1,19 @@
import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faAnchor, faArrowRight } from '@fortawesome/free-solid-svg-icons'
import { Checkbox, Tooltip } from "@material-ui/core";
import { intersection } from "lodash";
import { action, autorun, computed, Lambda, observable } from "mobx";
import { observer } from "mobx-react";
import { ColorState, SketchPicker } from "react-color";
-import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, AclSelfEdit, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, Opt, WidthSym } from "../../fields/Doc";
+import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, AclSelfEdit, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, NumListCast, Opt, StrListCast, WidthSym } from "../../fields/Doc";
import { Id } from "../../fields/FieldSymbols";
import { InkField } from "../../fields/InkField";
import { List } from "../../fields/List";
import { ComputedField } from "../../fields/ScriptField";
import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types";
import { denormalizeEmail, GetEffectiveAcl, SharingPermissions } from "../../fields/util";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../Utils";
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from "../../Utils";
import { DocumentType } from "../documents/DocumentTypes";
import { CurrentUserUtils } from "../util/CurrentUserUtils";
import { DocumentManager } from "../util/DocumentManager";
@@ -31,6 +32,7 @@ import { PropertiesButtons } from "./PropertiesButtons";
import { PropertiesDocContextSelector } from "./PropertiesDocContextSelector";
import "./PropertiesView.scss";
import { DefaultStyleProvider } from "./StyleProvider";
+import { LinkManager } from "../util/LinkManager";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -57,6 +59,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@computed get isPres(): boolean {
return this.selectedDoc?.type === DocumentType.PRES;
}
+ @computed get isLink(): boolean {
+ return this.selectedDoc?.type === DocumentType.LINK;
+ }
@computed get dataDoc() { return this.selectedDoc?.[DataSym]; }
@observable layoutFields: boolean = false;
@@ -146,8 +151,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
if (key[0] === "#") {
rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "2px" }} key={key}>
<span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key}</span>
- &nbsp;
- </div>);
+ &nbsp;
+ </div>);
} else {
const contentElement = <EditableView key="editableView"
contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
@@ -214,8 +219,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "-1px" }} key={key}>
<span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ":"}</span>
- &nbsp;
- {contentElement}
+ &nbsp;
+ {contentElement}
</div>);
}
}
@@ -871,7 +876,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
onPointerDown={action(() => this.openOptions = !this.openOptions)}
style={{ backgroundColor: this.openOptions ? "black" : "" }}>
Options
- <div className="propertiesView-settings-title-icon">
+ <div className="propertiesView-settings-title-icon">
<FontAwesomeIcon icon={this.openOptions ? "caret-down" : "caret-right"} size="lg" color="white" />
</div>
</div>
@@ -888,7 +893,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
onPointerDown={action(() => this.openSharing = !this.openSharing)}
style={{ backgroundColor: this.openSharing ? "black" : "" }}>
Sharing {"&"} Permissions
- <div className="propertiesView-sharing-title-icon">
+ <div className="propertiesView-sharing-title-icon">
<FontAwesomeIcon icon={this.openSharing ? "caret-down" : "caret-right"} size="lg" color="white" />
</div>
</div>
@@ -964,7 +969,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
onPointerDown={action(() => this.openFilters = !this.openFilters)}
style={{ backgroundColor: this.openFilters ? "black" : "" }}>
Filters
- <div className="propertiesView-filters-title-icon">
+ <div className="propertiesView-filters-title-icon">
<FontAwesomeIcon icon={this.openFilters ? "caret-down" : "caret-right"} size="lg" color="white" />
</div>
</div>
@@ -1013,7 +1018,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
onPointerDown={action(() => this.openAppearance = !this.openAppearance)}
style={{ backgroundColor: this.openAppearance ? "black" : "" }}>
Appearance
- <div className="propertiesView-appearance-title-icon">
+ <div className="propertiesView-appearance-title-icon">
<FontAwesomeIcon icon={this.openAppearance ? "caret-down" : "caret-right"} size="lg" color="white" />
</div>
</div>
@@ -1028,7 +1033,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
onPointerDown={action(() => this.openTransform = !this.openTransform)}
style={{ backgroundColor: this.openTransform ? "black" : "" }}>
Transform
- <div className="propertiesView-transform-title-icon">
+ <div className="propertiesView-transform-title-icon">
<FontAwesomeIcon icon={this.openTransform ? "caret-down" : "caret-right"} size="lg" color="white" />
</div>
</div>
@@ -1045,7 +1050,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
onPointerDown={action(() => this.openFields = !this.openFields)}
style={{ backgroundColor: this.openFields ? "black" : "" }}>
Fields {"&"} Tags
- <div className="propertiesView-fields-title-icon">
+ <div className="propertiesView-fields-title-icon">
<FontAwesomeIcon icon={this.openFields ? "caret-down" : "caret-right"} size="lg" color="white" />
</div>
</div>
@@ -1066,7 +1071,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
onPointerDown={action(() => this.openContexts = !this.openContexts)}
style={{ backgroundColor: this.openContexts ? "black" : "" }}>
Contexts
- <div className="propertiesView-contexts-title-icon">
+ <div className="propertiesView-contexts-title-icon">
<FontAwesomeIcon icon={this.openContexts ? "caret-down" : "caret-right"} size="lg" color="white" />
</div>
</div>
@@ -1080,7 +1085,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
onPointerDown={action(() => this.openLayout = !this.openLayout)}
style={{ backgroundColor: this.openLayout ? "black" : "" }}>
Layout
- <div className="propertiesView-layout-title-icon">
+ <div className="propertiesView-layout-title-icon">
<FontAwesomeIcon icon={this.openLayout ? "caret-down" : "caret-right"} size="lg" color="white" />
</div>
</div>
@@ -1088,7 +1093,147 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
</div>;
}
+ @observable description = Field.toString(LinkManager.currentLink?.description as any as Field);
+ @observable relationship = StrCast(LinkManager.currentLink?.linkRelationship);
+ @observable private relationshipButtonColor: string = "";
+
+ // @action
+ // handleDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.description = e.target.value; }
+ // handleRelationshipChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.relationship = e.target.value; }
+
+ @undoBatch
+ handleDescriptionChange = action((value: string) => {
+ if (LinkManager.currentLink) {
+ this.selectedDoc.description = value;
+ this.description = value;
+ return true;
+ }
+ });
+ @undoBatch
+ handleLinkRelationshipChange = action((value: string) => {
+ if (LinkManager.currentLink) {
+ this.selectedDoc.linkRelationship = value;
+ this.relationship = value;
+ return true;
+ }
+ });
+
+ @undoBatch
+ setDescripValue = action((value: string) => {
+ if (LinkManager.currentLink) {
+ Doc.GetProto(LinkManager.currentLink).description = value;
+ return true;
+ }
+ });
+
+ @undoBatch
+ setLinkRelationshipValue = action((value: string) => {
+ if (LinkManager.currentLink) {
+ const prevRelationship = LinkManager.currentLink.linkRelationship as string;
+ LinkManager.currentLink.linkRelationship = value;
+ Doc.GetProto(LinkManager.currentLink).linkRelationship = value;
+ const linkRelationshipList = StrListCast(Doc.UserDoc().linkRelationshipList);
+ const linkRelationshipSizes = NumListCast(Doc.UserDoc().linkRelationshipSizes);
+ const linkColorList = StrListCast(Doc.UserDoc().linkColorList);
+
+ // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color
+ if (!linkRelationshipList?.includes(value)) {
+ linkRelationshipList.push(value);
+ linkRelationshipSizes.push(1);
+ const randColor = "rgb(" + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + ")";
+ linkColorList.push(randColor);
+ // if the relationship is already in the list AND the new rel is different from the prev rel, update the rel sizes
+ } else if (linkRelationshipList && value !== prevRelationship) {
+ const index = linkRelationshipList.indexOf(value);
+ //increment size of new relationship size
+ if (index !== -1 && index < linkRelationshipSizes.length) {
+ const pvalue = linkRelationshipSizes[index];
+ linkRelationshipSizes[index] = (pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue + 1);
+ }
+ //decrement the size of the previous relationship if it already exists (i.e. not default 'link' relationship upon link creation)
+ if (linkRelationshipList.includes(prevRelationship)) {
+ const pindex = linkRelationshipList.indexOf(prevRelationship);
+ if (pindex !== -1 && pindex < linkRelationshipSizes.length) {
+ const pvalue = linkRelationshipSizes[pindex];
+ linkRelationshipSizes[pindex] = Math.max(0, (pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue - 1));
+ }
+ }
+
+ }
+ this.relationshipButtonColor = "rgb(62, 133, 55)";
+ setTimeout(action(() => this.relationshipButtonColor = ""), 750);
+ return true;
+ }
+ });
+
+ @undoBatch
+ changeFollowBehavior = action((follow: string) => {
+ if (LinkManager.currentLink) {
+ this.selectedDoc.followLinkLocation = follow;
+ return true;
+ }
+ });
+
+ onSelectOutDesc = () => {
+ this.setDescripValue(this.description);
+ document.getElementById('link_description_input')?.blur();
+ }
+
+ onDescriptionKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
+ if (e.key === "Enter") {
+ this.setDescripValue(this.description);
+ document.getElementById('link_description_input')?.blur();
+ }
+ }
+
+ onSelectOutRelationship = () => {
+ this.setLinkRelationshipValue(this.relationship);
+ document.getElementById('link_relationship_input')?.blur();
+ }
+
+ onRelationshipKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
+ if (e.key === "Enter") {
+ this.setLinkRelationshipValue(this.relationship);
+ document.getElementById('link_relationship_input')?.blur();
+ }
+ }
+
+ toggleAnchor = (e: React.PointerEvent) => {
+ setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc.linkAutoMove = !this.selectedDoc.linkAutoMove)));
+ }
+
+ toggleArrow = (e: React.PointerEvent) => {
+ setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc.displayArrow = !this.selectedDoc.displayArrow)));
+ }
+
+ @computed
+ get editRelationship() {
+ return <input
+ autoComplete={"off"}
+ id="link_relationship_input"
+ value={StrCast(this.selectedDoc.linkRelationship)}
+ onKeyDown={this.onRelationshipKey}
+ onBlur={this.onSelectOutRelationship}
+ onChange={e => this.handleLinkRelationshipChange(e.currentTarget.value)}
+ className="text"
+ type="text"
+ />
+ }
+
+ @computed
+ get editDescription() {
+ return <input
+ autoComplete={"off"}
+ id="link_description_input"
+ value={StrCast(this.selectedDoc.description)}
+ onKeyDown={this.onDescriptionKey}
+ onBlur={this.onSelectOutDesc}
+ onChange={e => this.handleDescriptionChange(e.currentTarget.value)}
+ className="text"
+ type="text"
+ />
+ }
/**
* Handles adding and removing members from the sharing panel
@@ -1111,6 +1256,67 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
</div>;
} else {
+ if (this.selectedDoc && this.isLink) {
+ return <div className="propertiesView">
+ <div className="propertiesView-title">
+ Linking
+ </div>
+ <div className="propertiesView-section">
+ <p className="propertiesView-label">Information</p>
+ <div className="propertiesView-input first" id="propertiesView-category">
+ <p>Link Relationship</p>
+ {this.editRelationship}
+ </div>
+ <div className="propertiesView-input" id="propertiesView-description">
+ <p>Description</p>
+ {this.editDescription}
+ </div>
+ </div>
+ <div className="propertiesView-section">
+ <p className="propertiesView-label">Behavior</p>
+ <div className="propertiesView-input inline first" id="propertiesView-follow">
+ <p>Follow</p>
+ <select
+ name="selectList"
+ id="selectList"
+ onChange={e => this.changeFollowBehavior(e.currentTarget.value)}
+ value={StrCast(this.selectedDoc.followLinkLocation, "default")}>
+ <option value="default">Default</option>
+ <option value="add:left">Open in new left pane</option>
+ <option value="add:right">Open in new right pane</option>
+ <option value="replace:left">Replace left tab</option>
+ <option value="replace:right">Replace right tab</option>
+ <option value="fullScreen">Open full screen</option>
+ <option value="add">Open in new tab</option>
+ <option value="replace">Replace current tab</option>
+ {this.selectedDoc.linksToAnnotation
+ ? <option value="openExternal">Open in external page</option>
+ : null}
+ </select>
+ </div>
+ <div className="propertiesView-input inline" id="propertiesView-anchor">
+ <p>Auto-move anchor</p>
+ <button
+ style={{ background: this.selectedDoc.hidden ? "gray" : !this.selectedDoc.linkAutoMove ? "" : "#4476f7", borderRadius: 3 }}
+ onPointerDown={this.toggleAnchor} onClick={e => e.stopPropagation()}
+ className="propertiesButton"
+ >
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline" id="propertiesView-displayArrow">
+ <p>Display arrow</p>
+ <button
+ style={{ background: this.selectedDoc.hidden ? "gray" : !this.selectedDoc.displayArrow ? "" : "#4476f7", borderRadius: 3 }}
+ onPointerDown={this.toggleArrow} onClick={e => e.stopPropagation()}
+ className="propertiesButton"
+ >
+ <FontAwesomeIcon className="fa-icon" icon={faArrowRight} size="lg" />
+ </button>
+ </div>
+ </div>
+ </div >;
+ }
if (this.selectedDoc && !this.isPres) {
return <div className="propertiesView" style={{
width: this.props.width,
@@ -1164,7 +1370,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
onPointerDown={action(() => { this.openPresTransitions = !this.openPresTransitions; })}
style={{ backgroundColor: this.openPresTransitions ? "black" : "" }}>
&nbsp; <FontAwesomeIcon style={{ alignSelf: "center" }} icon={"rocket"} /> &nbsp; Transitions
- <div className="propertiesView-presTrails-title-icon">
+ <div className="propertiesView-presTrails-title-icon">
<FontAwesomeIcon icon={this.openPresTransitions ? "caret-down" : "caret-right"} size="lg" color="white" />
</div>
</div>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 9cc887e3d..c35bb3581 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -11,6 +11,8 @@ import { SnappingManager } from "../../../util/SnappingManager";
import { DocumentView } from "../../nodes/DocumentView";
import "./CollectionFreeFormLinkView.scss";
import React = require("react");
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
+import { Colors } from "../../global/globalEnums";
export interface CollectionFreeFormLinkViewProps {
@@ -138,6 +140,39 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
return left; //top <= document.documentElement.clientHeight && getComputedStyle(document.documentElement).overflow === "hidden";
}
+ @action
+ toggleProperties = () => {
+ if (CurrentUserUtils.propertiesWidth > 0) {
+ CurrentUserUtils.propertiesWidth = 0;
+ } else {
+ CurrentUserUtils.propertiesWidth = 250;
+ }
+ }
+
+ onClickLine = () => {
+ SelectionManager.SelectSchemaViewDoc(this.props.LinkDocs[0], true)
+ this.toggleProperties()
+ }
+
+ // componentToHex = (c: number) => {
+ // let hex = c.toString(16);
+ // return hex.length == 1 ? "0" + hex : hex;
+ // }
+
+ // rgbToHex = (rgbString: string) => {
+ // if (rgbString != "black") {
+ // const splitString = rgbString.split(/rgb|\(|\)|,| /)
+ // let values: number[] = []
+ // splitString.forEach(elt => {
+ // if (elt) {
+ // values.push(parseInt(elt))
+ // }
+ // })
+ // return "#" + this.componentToHex(values[0]) + this.componentToHex(values[1]) + this.componentToHex(values[2]);
+ // }
+ // return "#000000"
+ // }
+
@computed.struct get renderData() {
this._start; SnappingManager.GetIsDragging();
const { A, B, LinkDocs } = this.props;
@@ -208,15 +243,27 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
//access stroke color using index of the relationship in the color list (default black)
const stroke = currRelationshipIndex === -1 || currRelationshipIndex >= linkColorList.length ? "black" : linkColorList[currRelationshipIndex];
+ // const hexStroke = this.rgbToHex(stroke)
//calculate stroke width/thickness based on the relative importance of the relationshipship (i.e. how many links the relationship has)
//thickness varies linearly from 3px to 12px for increasing link count
const strokeWidth = linkSize === -1 ? "3px" : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + "px";
+ if (this.props.LinkDocs[0].displayArrow == undefined) {
+ this.props.LinkDocs[0].displayArrow = false;
+ }
+
return !a.width || !b.width || ((!this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<>
- <path className="collectionfreeformlinkview-linkLine" style={{ pointerEvents: "all", opacity: this._opacity, strokeDasharray: SelectionManager.SelectedSchemaDoc() === this.props.LinkDocs[0] ? "2 2" : undefined, stroke, strokeWidth }}
- onClick={() => SelectionManager.SelectSchemaViewDoc(this.props.LinkDocs[0], true)}
- d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`} />
+ <defs>
+ <marker id="arrowhead" markerWidth="4" markerHeight="3"
+ refX="0" refY="1.5" orient="auto">
+ <polygon points="0 0, 3 1.5, 0 3" fill={Colors.DARK_GRAY} />
+ </marker>
+ </defs>
+ <path className="collectionfreeformlinkview-linkLine" style={{ pointerEvents: "all", opacity: this._opacity, stroke: SelectionManager.SelectedSchemaDoc() === this.props.LinkDocs[0] ? Colors.MEDIUM_BLUE : stroke, strokeWidth }}
+ onClick={this.onClickLine}
+ d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
+ markerEnd={this.props.LinkDocs[0].displayArrow ? "url(#arrowhead)" : ""} />
{textX === undefined ? (null) : <text className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown} >
{Field.toString(this.props.LinkDocs[0].description as any as Field)}
</text>}
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index c35c52699..4d5ae1018 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -95,6 +95,7 @@ export const documentSchema = createSchema({
layers: listSpec("string"), // which layers the document is part of
_lockedPosition: "boolean", // whether the document can be moved (dragged)
_lockedTransform: "boolean",// whether a freeformview can pan/zoom
+ displayArrow: "boolean", // toggles directed arrows
// drag drop properties
_stayInCollection: "boolean",// whether document can be dropped into a different collection