From 065a29e87c771b5cd9301b0468fb036e070cad17 Mon Sep 17 00:00:00 2001
From: yunahi <60233430+yunahi@users.noreply.github.com>
Date: Fri, 10 Jul 2020 05:08:53 +0900
Subject: added format shape

---
 .../collectionFreeForm/FormatShapePane.tsx         | 996 +++++++++++++++++++++
 1 file changed, 996 insertions(+)
 create mode 100644 src/client/views/collections/collectionFreeForm/FormatShapePane.tsx

(limited to 'src/client/views/collections/collectionFreeForm/FormatShapePane.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
new file mode 100644
index 000000000..7b47b7ca4
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
@@ -0,0 +1,996 @@
+import React = require("react");
+import AntimodeMenu from "../../AntimodeMenu";
+import { observer } from "mobx-react";
+import { observable, action, computed } from "mobx";
+import "./FormatShapePane.scss";
+import { ActiveInkColor, ActiveInkBezierApprox, ActiveFillColor, ActiveArrowStart, ActiveArrowEnd, SetActiveInkWidth, SetActiveInkColor, SetActiveBezierApprox, SetActiveFillColor, SetActiveArrowStart, SetActiveArrowEnd, ActiveDash, SetActiveDash } from "../../InkingStroke";
+import { Scripting } from "../../../util/Scripting";
+import { InkTool, InkField } from "../../../../fields/InkField";
+import { ColorState } from "react-color";
+import { Utils } from "../../../../Utils";
+import GestureOverlay from "../../GestureOverlay";
+import { Doc } from "../../../../fields/Doc";
+import { SelectionManager } from "../../../util/SelectionManager";
+import { DocumentView } from "../../../views/nodes/DocumentView";
+import { Document } from "../../../../fields/documentSchemas";
+import { DocumentType } from "../../../documents/DocumentTypes";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
+import { faRulerCombined, faFillDrip, faPenNib } from "@fortawesome/free-solid-svg-icons";
+import { Cast, StrCast, BoolCast, NumCast } from "../../../../fields/Types";
+import e = require("express");
+import { ChangeEvent } from "mongodb";
+import { black } from "colors";
+
+
+library.add(faRulerCombined, faFillDrip, faPenNib);
+
+@observer
+export default class FormatShapePane extends AntimodeMenu {
+    static Instance: FormatShapePane;
+
+    private _palette = ["#D0021B", "#F5A623", "#F8E71C", "#8B572A", "#7ED321", "#417505", "#9013FE", "#4A90E2", "#50E3C2", "#B8E986", "#000000", "#4A4A4A", "#9B9B9B", "#FFFFFF"];
+    private _width = ["1", "5", "10", "100"];
+    private _mode = ["fill-drip", "ruler-combined"];
+    private _subMenu = ["fill", "line", "size", "position"];
+    private _headIcon = ["⎯", "←"];
+    private _endIcon = ["⎯", "→"];
+    private _head = ["none", "arrowHead"];
+    private _end = ["none", "arrowEnd"];
+
+    @observable private _subOpen = [false, false, false, false];
+
+    @observable private collapsed: boolean = false;
+    @observable private currMode: string = "fill-drip";
+    @observable _fillBtn = false;
+    @observable _lineBtn = false;
+
+    @observable _selectDoc: DocumentView[] = [];
+
+    @observable _noFill = false;
+    @observable _solidFill = false;
+    @observable _noLine = false;
+    @observable _solidLine = false;
+    @observable _dashLine = false;
+    @observable _lock = false;
+
+    @observable _multiple = false;
+
+    @observable _widthBtn = false;
+
+    @observable _currWidth = "10";
+
+    @observable _single = false;
+    @observable _currFill = "#D0021B";
+    @observable _currColor = "#D0021B";
+
+    @observable _arrowHead = false;
+    @observable _arrowEnd = false;
+
+
+    @observable _currSizeHeight = "10";
+    @observable _currSizeWidth = "10";
+    @observable _currRotation = "10";
+
+    @observable _currPositionHorizontal = "10";
+    @observable _currPositionVertical = "10";
+
+
+    constructor(props: Readonly<{}>) {
+        super(props);
+        FormatShapePane.Instance = this;
+        this._canFade = false; // don't let the inking menu fade away
+        this.Pinned = BoolCast(Doc.UserDoc()["formatShapePane-pinned"]);
+
+    }
+
+    @action
+    toggleMenuPin = (e: React.MouseEvent) => {
+        Doc.UserDoc()["formatShapePane-pinned"] = this.Pinned = !this.Pinned;
+        if (!this.Pinned) {
+            // this.fadeOut(true);
+        }
+    }
+
+    @action
+    protected toggleCollapse = (e: React.MouseEvent) => {
+        this.collapsed = !this.collapsed;
+        setTimeout(() => {
+            const x = Math.min(this._left, window.innerWidth - FormatShapePane.Instance.width);
+            FormatShapePane.Instance.jumpTo(x, this._top, true);
+        }, 0);
+    }
+
+    @action
+    closePane = () => {
+        this.jumpTo(-300, -300);
+        this.Pinned = false;
+
+    }
+
+    @action
+    checkSame = () => {
+        const docs = SelectionManager.SelectedDocuments();
+        const inks: DocumentView[] = [];
+        for (var i = 0; i < docs.length; i++) {
+            if (Document(docs[i].rootDoc).type === DocumentType.INK) {
+                inks.push(docs[i]);
+            }
+        }
+        const firstWidth = Document(inks[0].rootDoc).strokeWidth;
+        const firstColor = Document(inks[0].rootDoc).color;
+        const firstFill = Document(inks[0].rootDoc).fillColor;
+        // const firstBezier
+        const firstRotation = Document(inks[0].rootDoc).rotation;
+        const firstArrowStart = Document(inks[0].rootDoc).arrowStart;
+        const firstArrowEnd = Document(inks[0].rootDoc).arrowEnd;
+        const firstDash = Document(inks[0].rootDoc).dash;
+        const firstWidthSize = Document(inks[0].rootDoc)._width;
+        const firstHeightSize = Document(inks[0].rootDoc)._height;
+        const firstHorizontal = Document(inks[0].rootDoc).x;
+        const firstVertical = Document(inks[0].rootDoc).y;
+        this._noFill = Document(inks[0].rootDoc).fillColor === "none" ? true : false;
+        this._solidFill = Document(inks[0].rootDoc).fillColor === "none" ? false : true;
+        this._noLine = Document(inks[0].rootDoc).color === "none" ? true : false;
+        if (Document(inks[0].rootDoc).color !== "none") {
+            this._solidLine = true;
+            this._dashLine = true;
+            if (Document(inks[0].rootDoc).dash === "0") {
+                this._dashLine = false;
+            } else {
+                this._solidLine = false;
+
+            }
+        }
+        // this._solidLine = (Document(inks[0].rootDoc).color === "none") ? false : Document(inks[0].rootDoc).dash === 0 ? true : false;
+        // this._dashLine = (Document(inks[0].rootDoc).color === "none") ? false : Document(inks[0].rootDoc).dash !== 0 ? true : false;
+        // // this._widthBtn = false;    
+        this._currWidth = String(Document(inks[0].rootDoc).strokeWidth);
+        // this._single = false;
+        this._currFill = String(Document(inks[0].rootDoc).fillColor);
+        this._currColor = String(Document(inks[0].rootDoc).color);
+        this._arrowHead = Document(inks[0].rootDoc).arrowStart === "none" ? false : true;
+        this._arrowEnd = Document(inks[0].rootDoc).arrowEnd === "none" ? false : true;
+        this._currSizeHeight = String(Document(inks[0].rootDoc)._height);
+        this._currSizeWidth = String(Document(inks[0].rootDoc)._width);
+        this._currRotation = String(Document(inks[0].rootDoc).rotation);
+        this._currPositionHorizontal = String(Document(inks[0].rootDoc).x);
+        this._currPositionVertical = String(Document(inks[0].rootDoc).y);
+        for (var i = 0; i < inks.length; i++) {
+            if (Document(inks[i].rootDoc).strokeWidth !== firstWidth) {
+                this._currWidth = "";
+            }
+            if (Document(inks[i].rootDoc).color !== firstColor) {
+                this._noLine = false;
+                this._solidLine = false;
+                this._dashLine = false;
+            }
+            if (Document(inks[i].rootDoc).fillColor !== firstFill) {
+                this._solidFill = false;
+                this._noFill = false;
+            }
+            if (Document(inks[i].rootDoc).arrowStart !== firstArrowStart) {
+                this._arrowHead = false;
+            }
+            if (Document(inks[i].rootDoc).arrowEnd !== firstArrowEnd) {
+                this._arrowEnd = false;
+            }
+            if (Document(inks[i].rootDoc).x !== firstHorizontal) {
+                this._currPositionHorizontal = "";
+            }
+            if (Document(inks[i].rootDoc).y !== firstVertical) {
+                this._currPositionVertical = "";
+            }
+            if (Document(inks[i].rootDoc)._width !== firstWidthSize) {
+                this._currSizeWidth = "";
+            }
+            if (Document(inks[i].rootDoc)._height !== firstHeightSize) {
+                this._currSizeHeight = "";
+            }
+            if (Document(inks[i].rootDoc).rotation !== firstRotation) {
+                this._currRotation = "";
+            }
+        }
+
+
+
+    }
+
+    @action
+    upDownButtons = (dirs: string, field: string) => {
+        SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
+            const doc = Document(element.rootDoc);
+            if (doc.type === DocumentType.INK) {
+                switch (field) {
+                    case "width":
+                        if (doc.strokeWidth) {
+                            doc.strokeWidth = dirs === "up" ? doc.strokeWidth + 1 : doc.strokeWidth - 1;
+                            SetActiveInkWidth(String(doc.strokeWidth));
+
+                        }
+                        break;
+                    case "sizeWidth":
+                        if (doc._width && doc._height) {
+                            const oldWidth = doc._width;
+                            const oldHeight = doc._height;
+
+                            doc._width = dirs === "up" ? doc._width + 10 : doc._width - 10;
+                            if (this._lock) {
+                                doc._height = (doc._width * oldHeight) / oldWidth;
+                            }
+                        }
+                        break;
+                    case "sizeHeight":
+                        if (doc._width && doc._height) {
+                            const oldWidth = doc._width;
+                            const oldHeight = doc._height;
+                            doc._height = dirs === "up" ? doc._height + 10 : doc._height - 10;
+                            if (this._lock) {
+                                doc._width = (doc._height * oldWidth) / oldHeight;
+                            }
+                        }
+                        break;
+                    case "horizontal":
+                        if (doc.x) {
+                            doc.x = dirs === "up" ? doc.x + 10 : doc.x - 10;
+                        }
+                        break;
+                    case "vertical":
+                        if (doc.y) {
+                            doc.y = dirs === "up" ? doc.y + 10 : doc.y - 10;
+                        }
+                    case "rotation":
+
+                        this.rotate(dirs === "up" ? Number(doc.rotation) + Number(0.1) : Number(doc.rotation) - Number(0.1));
+
+                        break;
+                    default:
+                        break;
+                }
+                this.selected();
+
+            }
+        }));
+    }
+
+    @action
+    editProperties = (value: any, field: string) => {
+        SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
+            const doc = Document(element.rootDoc);
+            if (doc.type === DocumentType.INK) {
+                switch (field) {
+                    case "width":
+                        SetActiveInkWidth(value);
+                        doc.strokeWidth = Number(value);
+                        break;
+                    case "color":
+                        doc.color = String(value);
+                        break;
+                    case "fill":
+                        doc.fillColor = String(value);
+                        break;
+                    case "bezier":
+                        // doc.strokeBezier === 300 ? doc.strokeBezier = 0 : doc.strokeBezier = 300;
+                        break;
+                    case "arrowStart":
+                        doc.arrowStart = String(value);
+                        break;
+                    case "arrowEnd":
+                        doc.arrowEnd = String(value);
+                        break;
+                    case "dash":
+                        doc.dash = String(value);
+                        break;
+                    case "widthSize":
+                        if (doc._width && doc._height) {
+                            const oldWidth = doc._width;
+                            const oldHeight = doc._height;
+
+                            doc._width = Number(value);
+                            if (this._lock) {
+                                doc._height = (doc._width * oldHeight) / oldWidth;
+                            }
+                        }
+                        break;
+                    case "heightSize":
+                        if (doc._width && doc._height) {
+                            const oldWidth = doc._width;
+                            const oldHeight = doc._height;
+
+                            doc._height = Number(value);
+                            if (this._lock) {
+                                doc._width = (doc._height * oldWidth) / oldHeight;
+                            }
+                        }
+                        break;
+                    case "horizontal":
+                        doc.x = Number(value);
+                        break;
+                    case "vertical":
+                        doc.y = Number(value);
+
+                        break;
+                    default:
+                        break;
+                }
+            }
+            this.selected();
+        }));
+        this.checkSame();
+
+    }
+
+    @computed get close() {
+        const close = <button
+            className="antimodeMenu-button"
+            key="close"
+            onPointerDown={action(() => { this.closePane(); })}
+            style={{ right: 0, position: "absolute" }}>
+            X
+                </button>;
+        return close;
+    }
+
+    @computed get modes() {
+        const modes = <div className="antimodeMenu-button-tab">
+            {this._mode.map(mode => {
+                return <button
+                    className="antimodeMenu-button"
+                    key={mode}
+                    onPointerDown={action(() => { console.log("mode"); this.currMode = mode; })}
+                    style={{ backgroundColor: this.currMode === mode ? "121212" : "", position: "relative", top: 30 }}>
+                    <FontAwesomeIcon icon={mode as IconProp} size="lg" />
+                </button>;
+            })
+            }</div>;
+        return modes;
+    }
+
+    @action
+    selected = () => {
+        this._selectDoc = SelectionManager.SelectedDocuments();
+        if (this._selectDoc.length === 1 && Document(this._selectDoc[0].rootDoc).type === DocumentType.INK) {
+            this._single = true;
+            const doc = Document(this._selectDoc[0].rootDoc);
+            if (doc.type === DocumentType.INK) {
+                console.log(doc.fillColor);
+                if (doc.fillColor === "none") {
+                    this._noFill = true;
+                    this._solidFill = false;
+                } else {
+                    this._solidFill = true;
+                    this._noFill = false;
+                    this._currFill = String(doc.fillColor);
+                }
+                if (doc.color === "none") {
+                    this._noLine = true;
+                    this._solidLine = false;
+                    this._dashLine = false;
+                } else {
+                    console.log(doc.strokeWidth);
+                    this._currWidth = String(doc.strokeWidth);
+                    this._currColor = String(doc.color);
+                    if (doc.dash === "0") {
+                        this._solidLine = true;
+                        this._noLine = false;
+                        this._dashLine = false;
+                    } else {
+                        this._dashLine = true;
+                        this._noLine = false;
+                        this._solidLine = false;
+                    }
+
+                    this._arrowHead = doc.arrowStart === "none" ? false : true;
+                    this._arrowEnd = doc.arrowEnd === "none" ? false : true;
+
+
+                    this._currPositionHorizontal = String(doc.x);
+                    this._currPositionVertical = String(doc.y);
+                    console.log(doc.rotation);
+                    this._currRotation = String(doc.rotation);
+                    this._currSizeHeight = String(doc._height);
+                    this._currSizeWidth = String(doc._width);
+                }
+
+            }
+        } else {
+            this._noFill = false;
+            this._solidFill = false;
+            this._single = false;
+            this._currFill = "#D0021B";
+            this._noLine = false;
+            this._solidLine = false;
+            this._dashLine = false;
+            this._currColor = "#D0021B";
+            this._arrowHead = false;
+            this._arrowEnd = false;
+            this._currPositionHorizontal = "";
+            this._currPositionVertical = "";
+            this._currRotation = "";
+            this._currSizeHeight = "";
+            this._currSizeWidth = "";
+        }
+        this.checkSame();
+    }
+
+    @action
+    rotate = (degrees: number) => {
+        SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
+            const doc = Document(element.rootDoc);
+            if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
+                // doc.rotation = (Number(doc.rotation) + (Number(angle) * 180 / Math.PI));
+                // console.log(doc.rotation);
+                const angle = Number(degrees) - Number(doc.rotation);
+                doc.rotation = Number(degrees);
+                const ink = Cast(doc.data, InkField)?.inkData;
+                if (ink) {
+                    const xs = ink.map(p => p.X);
+                    const ys = ink.map(p => p.Y);
+                    const left = Math.min(...xs);
+                    const top = Math.min(...ys);
+                    const right = Math.max(...xs);
+                    const bottom = Math.max(...ys);
+                    const _centerPoints: { X: number, Y: number }[] = [];
+                    _centerPoints.push({ X: left, Y: top });
+
+                    const newPoints: { X: number, Y: number }[] = [];
+                    for (var i = 0; i < ink.length; i++) {
+                        const newX = Math.cos(angle) * (ink[i].X - _centerPoints[0].X) - Math.sin(angle) * (ink[i].Y - _centerPoints[0].Y) + _centerPoints[0].X;
+                        const newY = Math.sin(angle) * (ink[i].X - _centerPoints[0].X) + Math.cos(angle) * (ink[i].Y - _centerPoints[0].Y) + _centerPoints[0].Y;
+                        newPoints.push({ X: newX, Y: newY });
+                    }
+                    doc.data = new InkField(newPoints);
+                    const xs2 = newPoints.map(p => p.X);
+                    const ys2 = newPoints.map(p => p.Y);
+                    const left2 = Math.min(...xs2);
+                    const top2 = Math.min(...ys2);
+                    const right2 = Math.max(...xs2);
+                    const bottom2 = Math.max(...ys2);
+
+                    doc._height = (bottom2 - top2) * element.props.ScreenToLocalTransform().Scale;
+                    doc._width = (right2 - left2) * element.props.ScreenToLocalTransform().Scale;
+
+                }
+            }
+        }));
+    }
+
+    @action
+    toggle = (check: string) => {
+        switch (check) {
+            case "noFill":
+                if (!this._noFill) {
+                    this._noFill = true;
+                    this._solidFill = false;
+                }
+                break;
+            case "solidFill":
+                if (!this._solidFill) {
+                    this._solidFill = true;
+                    this._noFill = false;
+                    if (this._currFill === "none") {
+                        this._currFill = "#D0021B";
+                    }
+                }
+                break;
+            case "noLine":
+                if (!this._noLine) {
+                    this._noLine = true;
+                    this._solidLine = false;
+                    this._dashLine = false;
+                }
+                break;
+            case "solidLine":
+                if (!this._solidLine) {
+                    this._solidLine = true;
+                    this._noLine = false;
+                    this._dashLine = false;
+                    if (this._currColor === "none") {
+                        this._currColor = "#D0021B";
+                    }
+                }
+                break;
+            case "dashLine":
+                if (!this._dashLine) {
+                    this._dashLine = true;
+                    this._solidLine = false;
+                    this._noLine = false;
+                    if (this._currColor === "none") {
+                        this._currColor = "#D0021B";
+                    }
+                }
+                break;
+            case "lock":
+                if (this._lock) {
+                    this._lock = false;
+                } else {
+                    this._lock = true;
+                }
+                break;
+            case "arrowHead":
+                this._arrowHead = this._arrowHead ? false : true;
+                break;
+            case "arrowEnd":
+                this._arrowEnd = this._arrowEnd ? false : true;
+                break;
+            default:
+                break;
+        }
+    }
+
+    @computed get subMenu() {
+        const fillCheck = <div style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
+            <input id="nofill" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._noFill} onChange={action(() => { this.toggle("noFill"); this.editProperties("none", "fill"); })} />
+            No Fill
+            <br></br>
+            <input id="solidfill" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._solidFill} onChange={action(() => { this.toggle("solidFill"); this.editProperties(this._currFill, "fill"); })} />
+            Solid Fill
+            <br></br>
+            <br></br>
+            {this._solidFill ? "Color" : ""}
+            {this._solidFill ? this.fillButton : ""}
+            {this._fillBtn && this._solidFill ? this.fillPicker : ""}
+
+        </div>;
+        const arrows = <> <input id="arrowHead" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._arrowHead} onChange={action(() => { this.toggle("arrowHead"); this.editProperties(this._arrowHead ? "arrowHead" : "none", "arrowStart"); })} />
+         Arrow Head
+            <br></br>
+
+            <input id="arrowEnd" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._arrowEnd} onChange={action(() => { this.toggle("arrowEnd"); this.editProperties(this._arrowEnd ? "arrowEnd" : "none", "arrowEnd"); })} />
+         Arrow End
+            <br></br></>;
+        const lineCheck = <div style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
+            <input id="noLine" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._noLine} onChange={action(() => { this.toggle("noLine"); this.editProperties("none", "color"); })} />
+                No Line
+                <br></br>
+            <input id="solidLine" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._solidLine} onChange={action(() => { this.toggle("solidLine"); this.editProperties(this._currColor, "color"); this.editProperties("0", "dash"); })} />
+                Solid Line
+                <br></br>
+            <input id="dashLine" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._dashLine} onChange={action(() => { this.toggle("dashLine"); this.editProperties(this._currColor, "color"); this.editProperties("2", "dash"); })} />
+                Dash Line
+                <br></br>
+            <br></br>
+            {(this._solidLine || this._dashLine) ? "Color" : ""}
+            {(this._solidLine || this._dashLine) ? this.lineButton : ""}
+            {this._lineBtn && (this._solidLine || this._dashLine) ? this.linePicker : ""}
+            <br></br>
+            {(this._solidLine || this._dashLine) ? "Width" : ""}
+            {(this._solidLine || this._dashLine) ? this.widthInput : ""}
+            <br></br>
+            <br></br>
+            {(this._solidLine || this._dashLine) ? arrows : ""}
+
+        </div>;
+
+        const sizeCheck = <div style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
+            Height {this.sizeHeightInput}
+            <br></br>
+            <br></br>
+
+
+            Width {this.sizeWidthInput}
+            <br></br>
+            <br></br>
+
+            <input id="lock" style={{ width: "inherit", position: "absolute", right: 0 }} type="checkbox" checked={this._lock} onChange={action(() => { this.toggle("lock"); })} />
+                Lock Ratio
+            <br></br>
+            <br></br>
+
+
+            Rotation {this.rotationInput}
+            <br></br>
+            <br></br>
+
+
+        </div>;
+        const positionCheck = <div style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
+            Horizontal {this.positionHorizontalInput}
+            <br></br>
+            <br></br>
+
+            Vertical {this.positionVerticalInput}
+            <br></br>
+            <br></br>
+
+
+        </div>;
+
+        const subMenu = <div className="antimodeMenu-sub" style={{ position: "absolute", width: "inherit", top: 60 }}>
+            {this._subMenu.map((subMenu, i) => {
+                if (subMenu === "fill" || subMenu === "line") {
+                    return <div style={{ width: "inherit" }}><button
+                        className="antimodeMenu-button"
+                        key={subMenu}
+                        onPointerDown={action(() => { this._subOpen[i] = this._subOpen[i] ? false : true; })}
+                        style={{ backgroundColor: "121212", display: this.currMode === "fill-drip" ? "" : "none", width: "inherit", textAlign: "left" }}>
+                        {this._subOpen[i] ? "▼" : "▶︎"}
+                        {subMenu}
+                    </button>
+                        {this.currMode === "fill-drip" && subMenu === "fill" && this._subOpen[0] ? fillCheck : ""}
+                        {this.currMode === "fill-drip" && subMenu === "line" && this._subOpen[1] ? lineCheck : ""}
+
+                    </div>;
+                }
+                else if (subMenu === "size" || subMenu === "position") {
+                    return <div style={{ width: "inherit" }}><button
+                        className="antimodeMenu-button"
+                        key={subMenu}
+                        onPointerDown={action(() => { this._subOpen[i] = this._subOpen[i] ? false : true; })}
+                        style={{ backgroundColor: "121212", display: this.currMode === "ruler-combined" ? "" : "none", width: "inherit", textAlign: "left" }}>
+                        {this._subOpen[i] ? "▼" : "▶︎"}
+                        {subMenu}
+                    </button>
+                        {this.currMode === "ruler-combined" && subMenu === "size" && this._subOpen[2] ? sizeCheck : ""}
+                        {this.currMode === "ruler-combined" && subMenu === "position" && this._subOpen[3] ? positionCheck : ""}
+
+                    </div>
+                        ;
+
+                }
+            })
+            }</div>;
+        return subMenu;
+    }
+
+    @computed get fillButton() {
+        const fillButton = <><button
+            className="antimodeMenu-button"
+            key="fill"
+            title="fillChanger"
+            onPointerDown={action(e => this._fillBtn = !this._fillBtn)}
+            style={{
+                // backgroundColor: "121212",
+                position: "absolute", right: 80
+            }}>
+            <FontAwesomeIcon icon="fill-drip" size="lg" />
+            <div className="color-previewI" style={{ backgroundColor: this._currFill ?? "121212" }}></div>
+        </button>
+            <br></br>
+            <br></br></>;
+        return fillButton;
+    }
+    @computed get fillPicker() {
+        const fillPicker = <div className="btn-group-palette" key="fill" >
+            {this._palette.map(color => {
+                return <button
+                    className="antimodeMenu-button"
+                    key={color}
+                    onPointerDown={action(() => { this._currFill = color; this.editProperties(color, "fill"); })}
+                    style={{
+                        // backgroundColor: this._fillBtn ? "121212" : "",
+                        zIndex: 1001
+                    }}>
+                    <div className="color-previewII" style={{ backgroundColor: color }}></div>
+                </button>;
+            })}
+
+        </div>;
+        return fillPicker;
+    }
+
+    @computed get lineButton() {
+        const lineButton = <><button
+            className="antimodeMenu-button"
+            key="line"
+            title="lineChanger"
+            onPointerDown={action(e => this._lineBtn = !this._lineBtn)}
+            style={{
+                // backgroundColor: "121212",
+                position: "absolute", right: 80
+            }}>
+            <FontAwesomeIcon icon="pen-nib" size="lg" />
+            <div className="color-previewI" style={{ backgroundColor: this._currColor ?? "121212" }}></div>
+        </button>
+            <br></br>
+            <br></br></>;
+        return lineButton;
+    }
+    @computed get linePicker() {
+        const linePicker = <div className="btn-group-palette" key="line" >
+            {this._palette.map(color => {
+                return <button
+                    className="antimodeMenu-button"
+                    key={color}
+                    onPointerDown={action(() => { this._currColor = color; this.editProperties(color, "color"); })}
+                    style={{
+                        // backgroundColor: this._lineBtn ? "121212" : "",
+                        zIndex: 1001
+                    }}>
+                    <div className="color-previewII" style={{ backgroundColor: color }}></div>
+                </button>;
+            })}
+
+        </div>;
+        return linePicker;
+    }
+    @computed get widthInput() {
+        const widthInput = <>
+            <input
+                style={{ color: "black", width: 80, position: "absolute", right: 20 }}
+                type="text" value={this._currWidth}
+                onChange={e => this.onChange(e.target.value, "width")}
+                autoFocus></input>  <button
+                    className="antiMenu-Buttonup"
+                    key="up"
+                    onPointerDown={action(() => { this.upDownButtons("up", "width"); })}
+                    style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0 }}>
+                ˄
+            </button>
+            <br />
+            <button
+                className="antiMenu-Buttonup"
+                key="up"
+                onPointerDown={action(() => { this.upDownButtons("down", "width"); })}
+                style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0, marginTop: -8 }}>
+                ˅
+            </button></>;
+        return widthInput;
+    }
+    @computed get sizeHeightInput() {
+        const sizeHeightInput = <>
+            <input
+                style={{ color: "black", width: 80, position: "absolute", right: 20 }}
+                type="text" value={this._currSizeHeight}
+                onChange={e => this.onChange(e.target.value, "sizeHeight")}
+                autoFocus></input>
+            <button
+                className="antiMenu-Buttonup"
+                key="up"
+                onPointerDown={action(() => { this.upDownButtons("up", "sizeHeight"); })}
+                style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0 }}>
+                ˄
+            </button>
+            <br />
+            <button
+                className="antiMenu-Buttonup"
+                key="up"
+                onPointerDown={action(() => { this.upDownButtons("down", "sizeHeight"); })}
+                style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0, marginTop: -8 }}>
+                ˅
+            </button>
+        </>;
+        return sizeHeightInput;
+    }
+
+    @computed get sizeWidthInput() {
+        const sizeWidthInput = <>
+            <input
+                style={{ color: "black", width: 80, position: "absolute", right: 20 }}
+                type="text" value={this._currSizeWidth}
+                onChange={e => this.onChange(e.target.value, "sizeWidth")}
+                autoFocus></input>
+            <button
+                className="antiMenu-Buttonup"
+                key="up"
+                onPointerDown={action(() => { this.upDownButtons("up", "sizeWidth"); })}
+                style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0 }}>
+                ˄
+            </button>
+            <br></br>
+            <button
+                className="antiMenu-Buttonup"
+                key="up"
+                onPointerDown={action(() => { this.upDownButtons("down", "sizeWidth"); })}
+                style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0, marginTop: -8 }}>
+                ˅
+            </button></>;
+        return sizeWidthInput;
+    }
+
+    @computed get rotationInput() {
+        const rotationInput =
+            <>
+                <input
+                    style={{ color: "black", width: 80, position: "absolute", right: 20 }}
+                    type="text" value={this._currRotation}
+                    onChange={e => this.onChange(e.target.value, "rotation")}
+                    autoFocus></input>
+                <button
+                    className="antiMenu-Buttonup"
+                    key="up"
+                    onPointerDown={action(() => { this.upDownButtons("up", "rotation"); })}
+                    style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0 }}>
+                    ˄
+            </button>
+                <br></br>
+                <button
+                    className="antiMenu-Buttonup"
+                    key="up"
+                    onPointerDown={action(() => { this.upDownButtons("down", "rotation"); })}
+                    style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0, marginTop: -8 }}>
+                    ˅
+            </button></>;
+        return rotationInput;
+    }
+
+    @computed get positionHorizontalInput() {
+        const positionHorizontalInput =
+            <><input
+                style={{ color: "black", width: 80, position: "absolute", right: 20 }}
+                type="text" value={this._currPositionHorizontal}
+                onChange={e => this.onChange(e.target.value, "positionHorizontal")}
+                autoFocus></input>
+                <button
+                    className="antiMenu-Buttonup"
+                    key="up"
+                    onPointerDown={action(() => { this.upDownButtons("up", "horizontal"); })}
+                    style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0 }}>
+                    ˄
+            </button>
+                <br></br>
+                <button
+                    className="antiMenu-Buttonup"
+                    key="up"
+                    onPointerDown={action(() => { this.upDownButtons("down", "horizontal"); })}
+                    style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0, marginTop: -8 }}>
+                    ˅
+            </button></>;
+        return positionHorizontalInput;
+    }
+
+    @computed get positionVerticalInput() {
+        const positionVerticalInput =
+            <><input
+                style={{ color: "black", width: 80, position: "absolute", right: 20 }}
+                type="text" value={this._currPositionVertical}
+                onChange={e => this.onChange(e.target.value, "positionVertical")}
+                autoFocus></input>
+                <button
+                    className="antiMenu-Buttonup"
+                    key="up"
+                    onPointerDown={action(() => { this.upDownButtons("down", "vertical"); })}
+                    style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0 }}>
+                    ˄
+            </button>
+                <br></br>
+                <button
+                    className="antiMenu-Buttonup"
+                    key="up"
+                    onPointerDown={action(() => { this.upDownButtons("up", "vertical"); })}
+                    style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0, marginTop: -8 }}>
+                    ˅
+            </button></>;
+        return positionVerticalInput;
+    }
+
+
+    @action
+    onChange = (val: string, property: string): void => {
+        if (!Number.isNaN(Number(val)) && Number(val) !== null && val !== " ") {
+
+            // if (val === "") {
+            //     val = "0";
+            // }
+            switch (property) {
+                case "width":
+                    this._currWidth = val;
+                    if (val !== "") {
+                        this.editProperties(this._currWidth, "width");
+                    }
+                    break;
+                case "sizeHeight":
+                    this._currSizeHeight = val;
+                    if (val !== "") {
+                        this.editProperties(this._currSizeHeight, "heightSize");
+                    }
+                    break;
+                case "sizeWidth":
+                    this._currSizeWidth = val;
+                    if (val !== "") {
+
+                        this.editProperties(this._currSizeWidth, "widthSize");
+                    }
+                    break;
+                case "rotation":
+
+                    this._currRotation = val;
+                    if (val !== "") {
+
+                        this.rotate(Number(val));
+                    }
+                    break;
+                case "positionHorizontal":
+                    this._currPositionHorizontal = val; if (val !== "") {
+
+                        this.editProperties(this._currPositionHorizontal, "horizontal");
+                    }
+
+                    break;
+                case "positionVertical":
+                    this._currPositionVertical = val;
+                    if (val !== "") {
+
+                        this.editProperties(this._currPositionVertical, "vertical");
+                    }
+
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+
+
+
+    // @action
+    // changeWidth = (event: React.ChangeEvent<HTMLInputElement>) => {
+    //     console.log(event.target.value);
+    //     this._currWidth = NumCast(event.target.value);
+
+    // }
+
+    @computed get upDown() {
+        const upDown = <div>
+            <button
+                className="antiMenu-Button"
+                key="up"
+                onPointerDown={action(() => { console.log("up"); })}>
+                up
+            </button>
+            <br></br>
+            <button
+                className="antiMenu-Button"
+                key="up"
+                onPointerDown={action(() => { console.log("dpwm"); })}>
+                down
+            </button>
+        </div>;
+        return upDown;
+    }
+
+
+    @computed get widthPicker() {
+        var widthPicker = <button
+            className="antimodeMenu-button"
+            key="width"
+            onPointerDown={action(e => this._widthBtn = !this._widthBtn)}
+            style={{ backgroundColor: this._widthBtn ? "121212" : "" }}>
+            <FontAwesomeIcon icon="bars" size="lg" />
+        </button>;
+        if (this._widthBtn) {
+            widthPicker = <div className="btn2-group" key="width">
+                {widthPicker}
+                {this._width.map(wid => {
+                    return <button
+                        className="antimodeMenu-button"
+                        key={wid}
+                        onPointerDown={action(() => { SetActiveInkWidth(wid); this._widthBtn = false; this.editProperties(wid, "width"); })}
+                        style={{ backgroundColor: this._widthBtn ? "121212" : "" }}>
+                        {wid}
+                    </button>;
+                })}
+            </div>;
+        }
+        return widthPicker;
+    }
+
+
+
+    render() {
+        const buttons = [
+
+            this.close,
+            this.modes,
+            this.subMenu
+
+        ];
+
+        return this.getElementVert(buttons);
+    }
+}
+Scripting.addGlobal(function activatePen2(penBtn: any) {
+    if (penBtn) {
+        //no longer changes to inkmode
+        // Doc.SetSelectedTool(InkTool.Pen);
+        FormatShapePane.Instance.jumpTo(300, 300);
+        FormatShapePane.Instance.Pinned = true;
+
+    } else {
+        // Doc.SetSelectedTool(InkTool.None);
+        FormatShapePane.Instance.Pinned = false;
+        FormatShapePane.Instance.fadeOut(true);
+
+    }
+});
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From c8454d675a3081a83c56cf598b1b3637479b0459 Mon Sep 17 00:00:00 2001
From: yunahi <60233430+yunahi@users.noreply.github.com>
Date: Fri, 10 Jul 2020 05:22:06 +0900
Subject: added format shape

---
 src/client/util/SelectionManager.ts                |   3 +-
 .../collectionFreeForm/FormatShapePane.tsx         | 137 ++++-----------------
 2 files changed, 24 insertions(+), 116 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/FormatShapePane.tsx')

diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index aea2c542b..af2f320d3 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -15,8 +15,7 @@ export namespace SelectionManager {
         SelectedDocuments: ObservableMap<DocumentView, boolean> = new ObservableMap();
         @action
         SelectDoc(docView: DocumentView, ctrlPressed: boolean): void {
-            console.log("select"
-            );
+
             // if doc is not in SelectedDocuments, add it
             if (!manager.SelectedDocuments.get(docView)) {
                 if (!ctrlPressed) {
diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
index 7b47b7ca4..37ba805b6 100644
--- a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
+++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
@@ -3,12 +3,9 @@ import AntimodeMenu from "../../AntimodeMenu";
 import { observer } from "mobx-react";
 import { observable, action, computed } from "mobx";
 import "./FormatShapePane.scss";
-import { ActiveInkColor, ActiveInkBezierApprox, ActiveFillColor, ActiveArrowStart, ActiveArrowEnd, SetActiveInkWidth, SetActiveInkColor, SetActiveBezierApprox, SetActiveFillColor, SetActiveArrowStart, SetActiveArrowEnd, ActiveDash, SetActiveDash } from "../../InkingStroke";
+import { SetActiveInkWidth } from "../../InkingStroke";
 import { Scripting } from "../../../util/Scripting";
-import { InkTool, InkField } from "../../../../fields/InkField";
-import { ColorState } from "react-color";
-import { Utils } from "../../../../Utils";
-import GestureOverlay from "../../GestureOverlay";
+import { InkField } from "../../../../fields/InkField";
 import { Doc } from "../../../../fields/Doc";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { DocumentView } from "../../../views/nodes/DocumentView";
@@ -19,9 +16,6 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
 import { faRulerCombined, faFillDrip, faPenNib } from "@fortawesome/free-solid-svg-icons";
 import { Cast, StrCast, BoolCast, NumCast } from "../../../../fields/Types";
 import e = require("express");
-import { ChangeEvent } from "mongodb";
-import { black } from "colors";
-
 
 library.add(faRulerCombined, faFillDrip, faPenNib);
 
@@ -33,63 +27,42 @@ export default class FormatShapePane extends AntimodeMenu {
     private _width = ["1", "5", "10", "100"];
     private _mode = ["fill-drip", "ruler-combined"];
     private _subMenu = ["fill", "line", "size", "position"];
-    private _headIcon = ["⎯", "←"];
-    private _endIcon = ["⎯", "→"];
-    private _head = ["none", "arrowHead"];
-    private _end = ["none", "arrowEnd"];
-
     @observable private _subOpen = [false, false, false, false];
-
     @observable private collapsed: boolean = false;
     @observable private currMode: string = "fill-drip";
     @observable _fillBtn = false;
     @observable _lineBtn = false;
-
     @observable _selectDoc: DocumentView[] = [];
-
     @observable _noFill = false;
     @observable _solidFill = false;
     @observable _noLine = false;
     @observable _solidLine = false;
     @observable _dashLine = false;
     @observable _lock = false;
-
     @observable _multiple = false;
-
     @observable _widthBtn = false;
-
-    @observable _currWidth = "10";
-
     @observable _single = false;
-    @observable _currFill = "#D0021B";
-    @observable _currColor = "#D0021B";
-
     @observable _arrowHead = false;
     @observable _arrowEnd = false;
-
-
     @observable _currSizeHeight = "10";
     @observable _currSizeWidth = "10";
     @observable _currRotation = "10";
-
     @observable _currPositionHorizontal = "10";
     @observable _currPositionVertical = "10";
-
+    @observable _currWidth = "10";
+    @observable _currFill = "#D0021B";
+    @observable _currColor = "#D0021B";
 
     constructor(props: Readonly<{}>) {
         super(props);
         FormatShapePane.Instance = this;
-        this._canFade = false; // don't let the inking menu fade away
+        this._canFade = false;
         this.Pinned = BoolCast(Doc.UserDoc()["formatShapePane-pinned"]);
-
     }
 
     @action
     toggleMenuPin = (e: React.MouseEvent) => {
         Doc.UserDoc()["formatShapePane-pinned"] = this.Pinned = !this.Pinned;
-        if (!this.Pinned) {
-            // this.fadeOut(true);
-        }
     }
 
     @action
@@ -105,9 +78,9 @@ export default class FormatShapePane extends AntimodeMenu {
     closePane = () => {
         this.jumpTo(-300, -300);
         this.Pinned = false;
-
     }
 
+    //if multiple inks are selected and do not share the same prop, leave blank
     @action
     checkSame = () => {
         const docs = SelectionManager.SelectedDocuments();
@@ -117,18 +90,6 @@ export default class FormatShapePane extends AntimodeMenu {
                 inks.push(docs[i]);
             }
         }
-        const firstWidth = Document(inks[0].rootDoc).strokeWidth;
-        const firstColor = Document(inks[0].rootDoc).color;
-        const firstFill = Document(inks[0].rootDoc).fillColor;
-        // const firstBezier
-        const firstRotation = Document(inks[0].rootDoc).rotation;
-        const firstArrowStart = Document(inks[0].rootDoc).arrowStart;
-        const firstArrowEnd = Document(inks[0].rootDoc).arrowEnd;
-        const firstDash = Document(inks[0].rootDoc).dash;
-        const firstWidthSize = Document(inks[0].rootDoc)._width;
-        const firstHeightSize = Document(inks[0].rootDoc)._height;
-        const firstHorizontal = Document(inks[0].rootDoc).x;
-        const firstVertical = Document(inks[0].rootDoc).y;
         this._noFill = Document(inks[0].rootDoc).fillColor === "none" ? true : false;
         this._solidFill = Document(inks[0].rootDoc).fillColor === "none" ? false : true;
         this._noLine = Document(inks[0].rootDoc).color === "none" ? true : false;
@@ -142,11 +103,7 @@ export default class FormatShapePane extends AntimodeMenu {
 
             }
         }
-        // this._solidLine = (Document(inks[0].rootDoc).color === "none") ? false : Document(inks[0].rootDoc).dash === 0 ? true : false;
-        // this._dashLine = (Document(inks[0].rootDoc).color === "none") ? false : Document(inks[0].rootDoc).dash !== 0 ? true : false;
-        // // this._widthBtn = false;    
         this._currWidth = String(Document(inks[0].rootDoc).strokeWidth);
-        // this._single = false;
         this._currFill = String(Document(inks[0].rootDoc).fillColor);
         this._currColor = String(Document(inks[0].rootDoc).color);
         this._arrowHead = Document(inks[0].rootDoc).arrowStart === "none" ? false : true;
@@ -157,45 +114,43 @@ export default class FormatShapePane extends AntimodeMenu {
         this._currPositionHorizontal = String(Document(inks[0].rootDoc).x);
         this._currPositionVertical = String(Document(inks[0].rootDoc).y);
         for (var i = 0; i < inks.length; i++) {
-            if (Document(inks[i].rootDoc).strokeWidth !== firstWidth) {
+            if (Document(inks[i].rootDoc).strokeWidth !== Document(inks[0].rootDoc).strokeWidth) {
                 this._currWidth = "";
             }
-            if (Document(inks[i].rootDoc).color !== firstColor) {
+            if (Document(inks[i].rootDoc).color !== Document(inks[0].rootDoc).color) {
                 this._noLine = false;
                 this._solidLine = false;
                 this._dashLine = false;
             }
-            if (Document(inks[i].rootDoc).fillColor !== firstFill) {
+            if (Document(inks[i].rootDoc).fillColor !== Document(inks[0].rootDoc).fillColor) {
                 this._solidFill = false;
                 this._noFill = false;
             }
-            if (Document(inks[i].rootDoc).arrowStart !== firstArrowStart) {
+            if (Document(inks[i].rootDoc).arrowStart !== Document(inks[0].rootDoc).arrowStart) {
                 this._arrowHead = false;
             }
-            if (Document(inks[i].rootDoc).arrowEnd !== firstArrowEnd) {
+            if (Document(inks[i].rootDoc).arrowEnd !== Document(inks[0].rootDoc).arrowEnd) {
                 this._arrowEnd = false;
             }
-            if (Document(inks[i].rootDoc).x !== firstHorizontal) {
+            if (Document(inks[i].rootDoc).x !== Document(inks[0].rootDoc).x) {
                 this._currPositionHorizontal = "";
             }
-            if (Document(inks[i].rootDoc).y !== firstVertical) {
+            if (Document(inks[i].rootDoc).y !== Document(inks[0].rootDoc).y) {
                 this._currPositionVertical = "";
             }
-            if (Document(inks[i].rootDoc)._width !== firstWidthSize) {
+            if (Document(inks[i].rootDoc)._width !== Document(inks[0].rootDoc)._width) {
                 this._currSizeWidth = "";
             }
-            if (Document(inks[i].rootDoc)._height !== firstHeightSize) {
+            if (Document(inks[i].rootDoc)._height !== Document(inks[0].rootDoc)._height) {
                 this._currSizeHeight = "";
             }
-            if (Document(inks[i].rootDoc).rotation !== firstRotation) {
+            if (Document(inks[i].rootDoc).rotation !== Document(inks[0].rootDoc).rotation) {
                 this._currRotation = "";
             }
         }
-
-
-
     }
 
+
     @action
     upDownButtons = (dirs: string, field: string) => {
         SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
@@ -206,14 +161,12 @@ export default class FormatShapePane extends AntimodeMenu {
                         if (doc.strokeWidth) {
                             doc.strokeWidth = dirs === "up" ? doc.strokeWidth + 1 : doc.strokeWidth - 1;
                             SetActiveInkWidth(String(doc.strokeWidth));
-
                         }
                         break;
                     case "sizeWidth":
                         if (doc._width && doc._height) {
                             const oldWidth = doc._width;
                             const oldHeight = doc._height;
-
                             doc._width = dirs === "up" ? doc._width + 10 : doc._width - 10;
                             if (this._lock) {
                                 doc._height = (doc._width * oldHeight) / oldWidth;
@@ -240,19 +193,17 @@ export default class FormatShapePane extends AntimodeMenu {
                             doc.y = dirs === "up" ? doc.y + 10 : doc.y - 10;
                         }
                     case "rotation":
-
                         this.rotate(dirs === "up" ? Number(doc.rotation) + Number(0.1) : Number(doc.rotation) - Number(0.1));
-
                         break;
                     default:
                         break;
                 }
                 this.selected();
-
             }
         }));
     }
 
+
     @action
     editProperties = (value: any, field: string) => {
         SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
@@ -270,7 +221,6 @@ export default class FormatShapePane extends AntimodeMenu {
                         doc.fillColor = String(value);
                         break;
                     case "bezier":
-                        // doc.strokeBezier === 300 ? doc.strokeBezier = 0 : doc.strokeBezier = 300;
                         break;
                     case "arrowStart":
                         doc.arrowStart = String(value);
@@ -285,7 +235,6 @@ export default class FormatShapePane extends AntimodeMenu {
                         if (doc._width && doc._height) {
                             const oldWidth = doc._width;
                             const oldHeight = doc._height;
-
                             doc._width = Number(value);
                             if (this._lock) {
                                 doc._height = (doc._width * oldHeight) / oldWidth;
@@ -296,7 +245,6 @@ export default class FormatShapePane extends AntimodeMenu {
                         if (doc._width && doc._height) {
                             const oldWidth = doc._width;
                             const oldHeight = doc._height;
-
                             doc._height = Number(value);
                             if (this._lock) {
                                 doc._width = (doc._height * oldWidth) / oldHeight;
@@ -308,7 +256,6 @@ export default class FormatShapePane extends AntimodeMenu {
                         break;
                     case "vertical":
                         doc.y = Number(value);
-
                         break;
                     default:
                         break;
@@ -331,13 +278,14 @@ export default class FormatShapePane extends AntimodeMenu {
         return close;
     }
 
+    //select either coor&fill or size&position
     @computed get modes() {
         const modes = <div className="antimodeMenu-button-tab">
             {this._mode.map(mode => {
                 return <button
                     className="antimodeMenu-button"
                     key={mode}
-                    onPointerDown={action(() => { console.log("mode"); this.currMode = mode; })}
+                    onPointerDown={action(() => { this.currMode = mode; })}
                     style={{ backgroundColor: this.currMode === mode ? "121212" : "", position: "relative", top: 30 }}>
                     <FontAwesomeIcon icon={mode as IconProp} size="lg" />
                 </button>;
@@ -346,6 +294,7 @@ export default class FormatShapePane extends AntimodeMenu {
         return modes;
     }
 
+    //detects currently selected document and change value in pane
     @action
     selected = () => {
         this._selectDoc = SelectionManager.SelectedDocuments();
@@ -353,7 +302,6 @@ export default class FormatShapePane extends AntimodeMenu {
             this._single = true;
             const doc = Document(this._selectDoc[0].rootDoc);
             if (doc.type === DocumentType.INK) {
-                console.log(doc.fillColor);
                 if (doc.fillColor === "none") {
                     this._noFill = true;
                     this._solidFill = false;
@@ -382,11 +330,8 @@ export default class FormatShapePane extends AntimodeMenu {
 
                     this._arrowHead = doc.arrowStart === "none" ? false : true;
                     this._arrowEnd = doc.arrowEnd === "none" ? false : true;
-
-
                     this._currPositionHorizontal = String(doc.x);
                     this._currPositionVertical = String(doc.y);
-                    console.log(doc.rotation);
                     this._currRotation = String(doc.rotation);
                     this._currSizeHeight = String(doc._height);
                     this._currSizeWidth = String(doc._width);
@@ -418,8 +363,6 @@ export default class FormatShapePane extends AntimodeMenu {
         SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
             const doc = Document(element.rootDoc);
             if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
-                // doc.rotation = (Number(doc.rotation) + (Number(angle) * 180 / Math.PI));
-                // console.log(doc.rotation);
                 const angle = Number(degrees) - Number(doc.rotation);
                 doc.rotation = Number(degrees);
                 const ink = Cast(doc.data, InkField)?.inkData;
@@ -446,10 +389,8 @@ export default class FormatShapePane extends AntimodeMenu {
                     const top2 = Math.min(...ys2);
                     const right2 = Math.max(...xs2);
                     const bottom2 = Math.max(...ys2);
-
                     doc._height = (bottom2 - top2) * element.props.ScreenToLocalTransform().Scale;
                     doc._width = (right2 - left2) * element.props.ScreenToLocalTransform().Scale;
-
                 }
             }
         }));
@@ -854,14 +795,11 @@ export default class FormatShapePane extends AntimodeMenu {
         return positionVerticalInput;
     }
 
-
+    //change inputs
     @action
     onChange = (val: string, property: string): void => {
         if (!Number.isNaN(Number(val)) && Number(val) !== null && val !== " ") {
 
-            // if (val === "") {
-            //     val = "0";
-            // }
             switch (property) {
                 case "width":
                     this._currWidth = val;
@@ -912,35 +850,6 @@ export default class FormatShapePane extends AntimodeMenu {
     }
 
 
-
-
-    // @action
-    // changeWidth = (event: React.ChangeEvent<HTMLInputElement>) => {
-    //     console.log(event.target.value);
-    //     this._currWidth = NumCast(event.target.value);
-
-    // }
-
-    @computed get upDown() {
-        const upDown = <div>
-            <button
-                className="antiMenu-Button"
-                key="up"
-                onPointerDown={action(() => { console.log("up"); })}>
-                up
-            </button>
-            <br></br>
-            <button
-                className="antiMenu-Button"
-                key="up"
-                onPointerDown={action(() => { console.log("dpwm"); })}>
-                down
-            </button>
-        </div>;
-        return upDown;
-    }
-
-
     @computed get widthPicker() {
         var widthPicker = <button
             className="antimodeMenu-button"
-- 
cgit v1.2.3-70-g09d2


From 39836c84bb518b8497925434190309a1e9eafe55 Mon Sep 17 00:00:00 2001
From: yunahi <60233430+yunahi@users.noreply.github.com>
Date: Fri, 10 Jul 2020 05:25:46 +0900
Subject: added format shape

---
 src/client/views/ContextMenu.tsx                                    | 1 -
 src/client/views/InkingStroke.tsx                                   | 1 -
 src/client/views/MainView.tsx                                       | 1 -
 src/client/views/collections/collectionFreeForm/FormatShapePane.tsx | 1 -
 4 files changed, 4 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/FormatShapePane.tsx')

diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 1a6b7ab90..941d7b44a 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -224,7 +224,6 @@ export class ContextMenu extends React.Component {
     }
 
     render() {
-        console.log("context");
         if (!this._display) {
             return null;
         }
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 4a7ec40be..7d9a3c8b3 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -42,7 +42,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
 
     @action
     private formatShape = () => {
-        console.log("Clicked");
         FormatShapePane.Instance.Pinned = true;
         FormatShapePane.Instance.selected();
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 3eed44654..30ad621fd 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -453,7 +453,6 @@ export class MainView extends React.Component {
 
     @computed get mainContent() {
         const sidebar = this.userDoc?.["tabs-panelContainer"];
-        console.log('${ ANTIMODEMENU_HEIGHT }');
         return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
             <div className="mainView-mainContent" style={{
                 color: this.darkScheme ? "rgb(205,205,205)" : "black",
diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
index 37ba805b6..bf2218fdc 100644
--- a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
+++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
@@ -315,7 +315,6 @@ export default class FormatShapePane extends AntimodeMenu {
                     this._solidLine = false;
                     this._dashLine = false;
                 } else {
-                    console.log(doc.strokeWidth);
                     this._currWidth = String(doc.strokeWidth);
                     this._currColor = String(doc.color);
                     if (doc.dash === "0") {
-- 
cgit v1.2.3-70-g09d2


From 6b5c11a26e114f26f3907342f9afbd47b27cecf4 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 11 Jul 2020 17:54:20 -0400
Subject: restructured ink to make more react-friendly

---
 src/client/util/CurrentUserUtils.ts                |   6 +-
 src/client/util/InteractionUtils.tsx               |  21 +-
 src/client/util/SelectionManager.ts                |   3 -
 src/client/views/AntimodeMenu.tsx                  |   1 -
 src/client/views/DocumentDecorations.tsx           |   5 +-
 src/client/views/InkingStroke.tsx                  |  18 +-
 .../collectionFreeForm/FormatShapePane.tsx         | 801 +++++++--------------
 .../collectionFreeForm/InkOptionsMenu.tsx          |  24 +-
 src/client/views/nodes/ColorBox.tsx                |   1 -
 9 files changed, 299 insertions(+), 581 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/FormatShapePane.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index d0ca0e57e..0b644fa79 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -760,9 +760,9 @@ export class CurrentUserUtils {
         doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)");
         doc.activeInkWidth = StrCast(doc.activeInkWidth, "1");
         doc.activeInkBezier = StrCast(doc.activeInkBezier, "0");
-        doc.activeFillColor = StrCast(doc.activeFillColor, "none");
-        doc.activeArrowStart = StrCast(doc.activeArrowStart, "none");
-        doc.activeArrowEnd = StrCast(doc.activeArrowEnd, "none");
+        doc.activeFillColor = StrCast(doc.activeFillColor, "");
+        doc.activeArrowStart = StrCast(doc.activeArrowStart, "");
+        doc.activeArrowEnd = StrCast(doc.activeArrowEnd, "");
         doc.activeDash = StrCast(doc.activeDash, "0");
         doc.fontSize = NumCast(doc.fontSize, 12);
         doc["constants-snapThreshold"] = NumCast(doc["constants-snapThreshold"], 10); // 
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 2883e2056..6ab7bcc87 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -3,7 +3,6 @@ import * as beziercurve from 'bezier-curve';
 import * as fitCurve from 'fit-curve';
 import "./InteractionUtils.scss";
 import { Utils } from "../../Utils";
-import InkOptionsMenu from "../views/collections/collectionFreeForm/InkOptionsMenu";
 
 export namespace InteractionUtils {
     export const MOUSETYPE = "mouse";
@@ -96,12 +95,6 @@ export namespace InteractionUtils {
         color: string, width: number, strokeWidth: number, bezier: string, fill: string, arrowStart: string, arrowEnd: string,
         dash: string, scalex: number, scaley: number, shape: string, pevents: string, drawHalo: boolean, nodefs: boolean) {
 
-        // if (InkOptionsMenu.Instance.Pinned) {
-        //     for (var i = 0; i < points.length; i++) {
-        //         points[i].Y -= 35;
-        //     }
-        // }
-
         let pts: { X: number; Y: number; }[] = [];
         if (shape) { //if any of the shape are true
             pts = makePolygon(shape, points);
@@ -127,15 +120,15 @@ export namespace InteractionUtils {
         const dashArray = String(Number(width) * Number(dash));
         const defGuid = Utils.GenerateGuid();
         const arrowDim = Math.max(0.5, 8 / Math.log(Math.max(2, strokeWidth)));
-        return (<svg fill={fill === "none" ? color : fill}> {/* setting the svg fill sets the arrowhead fill */}
+        return (<svg fill={!fill || fill === "none" ? color : fill}> {/* setting the svg fill sets the arrowStart fill */}
             {nodefs ? (null) : <defs>
                 {arrowStart !== "dot" && arrowEnd !== "dot" ? (null) : <marker id={`dot${defGuid}`} orient="auto" overflow="visible">
                     <circle r={1} fill="context-stroke" />
                 </marker>}
-                {arrowStart !== "arrowHead" && arrowEnd !== "arrowHead" ? (null) : <marker id={`arrowHead${defGuid}`} orient="auto" overflow="visible" refX="1.6" refY="0" markerWidth="10" markerHeight="7">
+                {arrowStart !== "arrow" && arrowEnd !== "arrow" ? (null) : <marker id={`arrowStart${defGuid}`} orient="auto" overflow="visible" refX="1.6" refY="0" markerWidth="10" markerHeight="7">
                     <polygon points={`${arrowDim} ${-Math.max(1, arrowDim / 2)}, ${arrowDim} ${Math.max(1, arrowDim / 2)}, -1 0`} />
                 </marker>}
-                {arrowStart !== "arrowEnd" && arrowEnd !== "arrowEnd" ? (null) : <marker id={`arrowEnd${defGuid}`} orient="auto" overflow="visible" refX="1.6" refY="0" markerWidth="10" markerHeight="7">
+                {arrowStart !== "arrow" && arrowEnd !== "arrow" ? (null) : <marker id={`arrowEnd${defGuid}`} orient="auto" overflow="visible" refX="1.6" refY="0" markerWidth="10" markerHeight="7">
                     <polygon points={`${2 - arrowDim} ${-Math.max(1, arrowDim / 2)}, ${2 - arrowDim} ${Math.max(1, arrowDim / 2)}, 3 0`} />
                 </marker>}
             </defs>}
@@ -144,7 +137,7 @@ export namespace InteractionUtils {
                 points={strpts}
                 style={{
                     filter: drawHalo ? "url(#inkSelectionHalo)" : undefined,
-                    fill,
+                    fill: fill ? fill : "transparent",
                     opacity: strokeWidth !== width ? 0.5 : undefined,
                     pointerEvents: pevents as any,
                     stroke: color ?? "rgb(0, 0, 0)",
@@ -153,8 +146,8 @@ export namespace InteractionUtils {
                     strokeLinecap: "round",
                     strokeDasharray: dashArray
                 }}
-                markerStart={`url(#${arrowStart + defGuid})`}
-                markerEnd={`url(#${arrowEnd + defGuid})`}
+                markerStart={`url(#${arrowStart + "Start" + defGuid})`}
+                markerEnd={`url(#${arrowEnd + "End" + defGuid})`}
             />
 
         </svg>);
@@ -163,7 +156,7 @@ export namespace InteractionUtils {
     // export function makeArrow() {
     //     return (
     //         InkOptionsMenu.Instance.getColors().map(color => {
-    //             const id1 = "arrowHeadTest" + color;
+    //             const id1 = "arrowStartTest" + color;
     //             console.log(color);
     //             <marker id={id1} orient="auto" overflow="visible" refX="0" refY="1" markerWidth="10" markerHeight="7">
     //                 <polygon points="0 0, 3 1, 0 2" fill={"#" + color} />
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index af2f320d3..9a968aeda 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -5,7 +5,6 @@ import { computedFn } from "mobx-utils";
 import { List } from "../../fields/List";
 import { Scripting } from "./Scripting";
 import { DocumentManager } from "./DocumentManager";
-import FormatShapePane from "../views/collections/collectionFreeForm/FormatShapePane";
 
 export namespace SelectionManager {
 
@@ -31,8 +30,6 @@ export namespace SelectionManager {
                 manager.SelectedDocuments.set(docView, true);
             }
             Doc.UserDoc().activeSelection = new List(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
-            FormatShapePane.Instance.selected();
-
         }
         @action
         DeselectDoc(docView: DocumentView): void {
diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx
index cb6de1785..3303a5e68 100644
--- a/src/client/views/AntimodeMenu.tsx
+++ b/src/client/views/AntimodeMenu.tsx
@@ -1,7 +1,6 @@
 import React = require("react");
 import { observable, action } from "mobx";
 import "./AntimodeMenu.scss";
-import { inherits } from "util";
 
 /**
  * This is an abstract class that serves as the base for a PDF-style or Marquee-style
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 8edd4c4a9..81b4f9162 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -152,8 +152,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         if (e.button === 0 && !e.altKey && !e.ctrlKey) {
             let child = SelectionManager.SelectedDocuments()[0].ContentDiv!.children[0];
             while (child.children.length) {
-                const next = Array.from(child.children).find(c => !c.className.includes("collectionViewChrome"));
-                if (next?.className.includes("documentView-node")) break;
+                const next = Array.from(child.children).find(c => typeof (c.className) !== "string" || !c.className.includes("collectionViewChrome"));
+                if (typeof (next?.className) === "string" && next?.className.includes("documentView-node")) break;
                 if (next) child = next;
                 else break;
             }
@@ -294,7 +294,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             const doc = Document(element.rootDoc);
             if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
                 doc.rotation = Number(doc.rotation) + Number(angle);
-                console.log(doc.rotation);
                 const ink = Cast(doc.data, InkField)?.inkData;
                 if (ink) {
 
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 7d9a3c8b3..224abfd77 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -43,8 +43,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
     @action
     private formatShape = () => {
         FormatShapePane.Instance.Pinned = true;
-        FormatShapePane.Instance.selected();
-
     }
 
     render() {
@@ -62,14 +60,14 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         const height = bottom - top;
         const scaleX = (this.props.PanelWidth() - strokeWidth) / (width - strokeWidth);
         const scaleY = (this.props.PanelHeight() - strokeWidth) / (height - strokeWidth);
-        const strokeColor = StrCast(this.layoutDoc.color, ActiveInkColor());
+        const strokeColor = StrCast(this.layoutDoc.color, "");
         const points = InteractionUtils.CreatePolyline(data, left, top, strokeColor, strokeWidth, strokeWidth,
-            StrCast(this.layoutDoc.strokeBezier, ActiveInkBezierApprox()), StrCast(this.layoutDoc.fillColor, ActiveFillColor()),
-            StrCast(this.layoutDoc.arrowStart, ActiveArrowStart()), StrCast(this.layoutDoc.arrowEnd, ActiveArrowEnd()),
-            StrCast(this.layoutDoc.dash, ActiveDash()), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5, false);
+            StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"),
+            StrCast(this.layoutDoc.arrowStart), StrCast(this.layoutDoc.arrowEnd),
+            StrCast(this.layoutDoc.dash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5, false);
         const hpoints = InteractionUtils.CreatePolyline(data, left, top,
             this.props.isSelected() && strokeWidth > 5 ? strokeColor : "transparent", strokeWidth, (strokeWidth + 15),
-            StrCast(this.layoutDoc.strokeBezier, ActiveInkBezierApprox()), StrCast(this.layoutDoc.fillColor, ActiveFillColor()),
+            StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"),
             "none", "none", "0", scaleX, scaleY, "", this.props.active() ? "visiblepainted" : "none", false, true);
         return (
             <svg className="inkingStroke"
@@ -106,9 +104,9 @@ export function SetActiveArrowEnd(value: string) { ActiveInkPen() && (ActiveInkP
 export function SetActiveDash(dash: string): void { !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash); }
 export function ActiveInkPen(): Doc { return Cast(Doc.UserDoc().activeInkPen, Doc, null); }
 export function ActiveInkColor(): string { return StrCast(ActiveInkPen()?.activeInkColor, "black"); }
-export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activeFillColor, "none"); }
-export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, "none"); }
-export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, "none"); }
+export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activeFillColor, ""); }
+export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ""); }
+export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, ""); }
 export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash, "0"); }
 export function ActiveInkWidth(): string { return StrCast(ActiveInkPen()?.activeInkWidth, "1"); }
 export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); }
diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
index bf2218fdc..321b5648e 100644
--- a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
+++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
@@ -3,10 +3,9 @@ import AntimodeMenu from "../../AntimodeMenu";
 import { observer } from "mobx-react";
 import { observable, action, computed } from "mobx";
 import "./FormatShapePane.scss";
-import { SetActiveInkWidth } from "../../InkingStroke";
 import { Scripting } from "../../../util/Scripting";
 import { InkField } from "../../../../fields/InkField";
-import { Doc } from "../../../../fields/Doc";
+import { Doc, Opt } from "../../../../fields/Doc";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { DocumentView } from "../../../views/nodes/DocumentView";
 import { Document } from "../../../../fields/documentSchemas";
@@ -15,7 +14,6 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
 import { faRulerCombined, faFillDrip, faPenNib } from "@fortawesome/free-solid-svg-icons";
 import { Cast, StrCast, BoolCast, NumCast } from "../../../../fields/Types";
-import e = require("express");
 
 library.add(faRulerCombined, faFillDrip, faPenNib);
 
@@ -27,31 +25,131 @@ export default class FormatShapePane extends AntimodeMenu {
     private _width = ["1", "5", "10", "100"];
     private _mode = ["fill-drip", "ruler-combined"];
     private _subMenu = ["fill", "line", "size", "position"];
+    @computed get inks() {
+        const inks: DocumentView[] = [];
+        const docs = SelectionManager.SelectedDocuments();
+        for (var i = 0; i < docs.length; i++) {
+            if (Document(docs[i].rootDoc).type === DocumentType.INK) {
+                inks.push(docs[i]);
+            }
+        }
+        return inks.length ? inks : undefined;
+    }
     @observable private _subOpen = [false, false, false, false];
     @observable private collapsed: boolean = false;
     @observable private currMode: string = "fill-drip";
+    @observable _lock = false;
     @observable _fillBtn = false;
     @observable _lineBtn = false;
-    @observable _selectDoc: DocumentView[] = [];
-    @observable _noFill = false;
-    @observable _solidFill = false;
-    @observable _noLine = false;
-    @observable _solidLine = false;
-    @observable _dashLine = false;
-    @observable _lock = false;
-    @observable _multiple = false;
-    @observable _widthBtn = false;
-    @observable _single = false;
-    @observable _arrowHead = false;
-    @observable _arrowEnd = false;
-    @observable _currSizeHeight = "10";
-    @observable _currSizeWidth = "10";
-    @observable _currRotation = "10";
-    @observable _currPositionHorizontal = "10";
-    @observable _currPositionVertical = "10";
-    @observable _currWidth = "10";
-    @observable _currFill = "#D0021B";
-    @observable _currColor = "#D0021B";
+    _lastFill = "#D0021B";
+    _lastLine = "#D0021B";
+    _lastDash = "2";
+
+    @computed get _noFill() {
+        return this.inks?.reduce((p, i) => p && !i.rootDoc.fillColor ? true : false, true) || false;
+    }
+    @computed get _solidFill() {
+        return this.inks?.reduce((p, i) => p && i.rootDoc.fillColor ? true : false, true) || false;
+    }
+    set _noFill(value) { this._currFill = value ? "" : this._lastFill; }
+    set _solidFill(value) { this._noFill = !value; }
+
+    @computed get _noLine() {
+        return this.inks?.reduce((p, i) => p && !i.rootDoc.color ? true : false, true) || false;
+    }
+    @computed get _solidLine() {
+        return this.inks?.reduce((p, i) => p &&
+            i.rootDoc.color && (i.rootDoc.dash === undefined || i.rootDoc.dash === "0") ? true : false, true) || false;
+    }
+    @computed get _dashLine() {
+        return !this._noLine && this.inks?.reduce((p, i) =>
+            (p === undefined || (p && p === i.rootDoc.dash)) && i.rootDoc.dash !== "0" ? StrCast(i.rootDoc.dash) : "", undefined as Opt<string>) || "";
+    }
+    set _noLine(value) { this._currColor = value ? "" : this._lastLine; }
+    set _solidLine(value) { this._dashLine = ""; this._noLine = !value; }
+    set _dashLine(value) {
+        value && (this._lastDash = value); this._noLine = false;
+        this.inks?.forEach(i => i.rootDoc.dash = value ? this._lastDash : undefined)
+    }
+
+    @computed get _currFill() {
+        const cfill = this._noFill || !this.inks ? "" : StrCast(this.inks[0].rootDoc.fillColor);
+        cfill && (this._lastFill = cfill);
+        return cfill;
+    }
+    @computed get _currColor() {
+        const ccol = this._noLine || !this.inks ? "" : StrCast(this.inks[0].rootDoc.color, "");
+        this._lastLine = ccol ? ccol : this._lastLine;
+        return ccol;
+    }
+    set _currFill(value) { value && (this._lastFill = value); this.inks?.forEach(i => i.rootDoc.fillColor = value); }
+    set _currColor(value) { value && (this._lastLine = value); this.inks?.forEach(i => i.rootDoc.color = value ? value : undefined) }
+
+    @computed get _arrowStart() {
+        return this.inks?.reduce((p, i) =>
+            (p === undefined || (p && p === i.rootDoc.arrowStart)) ? StrCast(i.rootDoc.arrowStart) : "", undefined as Opt<string>) || "";
+    }
+    @computed get _arrowEnd() {
+        return this.inks?.reduce((p, i) =>
+            (p === undefined || (p && p === i.rootDoc.arrowEnd)) ? StrCast(i.rootDoc.arrowEnd) : "", undefined as Opt<string>) || ""
+    }
+    set _arrowStart(value) { this.inks?.forEach(i => i.rootDoc.arrowStart = value); }
+    set _arrowEnd(value) { this.inks?.forEach(i => i.rootDoc.arrowEnd = value); }
+
+    @computed get _currSizeHeight() {
+        return this.inks?.reduce((p, i) =>
+            (p === undefined || (p === NumCast(i.rootDoc._height).toString())) ? NumCast(i.rootDoc._height).toString() : "", undefined as Opt<string>) || ""
+    }
+    @computed get _currSizeWidth() {
+        return this.inks?.reduce((p, i) =>
+            (p === undefined || (p === NumCast(i.rootDoc._width).toString())) ? NumCast(i.rootDoc._width).toString() : "", undefined as Opt<string>) || ""
+    }
+    @computed get _currRotation() {
+        return this.inks?.reduce((p, i) =>
+            (p === undefined || (p === NumCast(i.rootDoc.rotation).toString())) ? NumCast(i.rootDoc.rotation).toString() : "", undefined as Opt<string>) || ""
+    }
+    @computed get _currPositionHorizontal() {
+        return this.inks?.reduce((p, i) =>
+            (p === undefined || (p === NumCast(i.rootDoc.x).toString())) ? NumCast(i.rootDoc.x).toString() : "", undefined as Opt<string>) || ""
+    }
+    @computed get _currPositionVertical() {
+        return this.inks?.reduce((p, i) =>
+            (p === undefined || (p === NumCast(i.rootDoc.y).toString())) ? NumCast(i.rootDoc.y).toString() : "", undefined as Opt<string>) || ""
+    }
+    @computed get _currStrokeWidth() {
+        return this.inks?.reduce((p, i) =>
+            (p === undefined || (p === NumCast(i.rootDoc.strokeWidth).toString())) ? NumCast(i.rootDoc.strokeWidth).toString() : "", undefined as Opt<string>) || ""
+    }
+    set _currPositionHorizontal(value) { this.inks?.forEach(i => i.rootDoc.x = Number(value)); }
+    set _currPositionVertical(value) { this.inks?.forEach(i => i.rootDoc.y = Number(value)); }
+    set _currRotation(value) { this.inks?.forEach(i => i.rootDoc.rotation = Number(value)); }
+    set _currStrokeWidth(value) { this.inks?.forEach(i => i.rootDoc.strokeWidth = Number(value)); }
+    set _currSizeWidth(value) {
+        this.inks?.forEach(i => {
+            const doc = i.rootDoc;
+            if (doc._width && doc._height) {
+                const oldWidth = NumCast(doc._width);
+                const oldHeight = NumCast(doc._height);
+                doc._width = Number(value);
+                if (this._lock) {
+                    doc._height = (doc._width * oldHeight) / oldWidth;
+                }
+            }
+        });
+    }
+    set _currSizeHeight(value) {
+        this.inks?.forEach(i => {
+            const doc = i.rootDoc;
+            if (doc._width && doc._height) {
+                const oldWidth = NumCast(doc._width);
+                const oldHeight = NumCast(doc._height);
+                doc._height = Number(value);
+                if (this._lock) {
+                    doc._width = (doc._height * oldWidth) / oldHeight;
+                }
+            }
+        });
+    }
 
     constructor(props: Readonly<{}>) {
         super(props);
@@ -80,191 +178,40 @@ export default class FormatShapePane extends AntimodeMenu {
         this.Pinned = false;
     }
 
-    //if multiple inks are selected and do not share the same prop, leave blank
-    @action
-    checkSame = () => {
-        const docs = SelectionManager.SelectedDocuments();
-        const inks: DocumentView[] = [];
-        for (var i = 0; i < docs.length; i++) {
-            if (Document(docs[i].rootDoc).type === DocumentType.INK) {
-                inks.push(docs[i]);
-            }
-        }
-        this._noFill = Document(inks[0].rootDoc).fillColor === "none" ? true : false;
-        this._solidFill = Document(inks[0].rootDoc).fillColor === "none" ? false : true;
-        this._noLine = Document(inks[0].rootDoc).color === "none" ? true : false;
-        if (Document(inks[0].rootDoc).color !== "none") {
-            this._solidLine = true;
-            this._dashLine = true;
-            if (Document(inks[0].rootDoc).dash === "0") {
-                this._dashLine = false;
-            } else {
-                this._solidLine = false;
-
-            }
-        }
-        this._currWidth = String(Document(inks[0].rootDoc).strokeWidth);
-        this._currFill = String(Document(inks[0].rootDoc).fillColor);
-        this._currColor = String(Document(inks[0].rootDoc).color);
-        this._arrowHead = Document(inks[0].rootDoc).arrowStart === "none" ? false : true;
-        this._arrowEnd = Document(inks[0].rootDoc).arrowEnd === "none" ? false : true;
-        this._currSizeHeight = String(Document(inks[0].rootDoc)._height);
-        this._currSizeWidth = String(Document(inks[0].rootDoc)._width);
-        this._currRotation = String(Document(inks[0].rootDoc).rotation);
-        this._currPositionHorizontal = String(Document(inks[0].rootDoc).x);
-        this._currPositionVertical = String(Document(inks[0].rootDoc).y);
-        for (var i = 0; i < inks.length; i++) {
-            if (Document(inks[i].rootDoc).strokeWidth !== Document(inks[0].rootDoc).strokeWidth) {
-                this._currWidth = "";
-            }
-            if (Document(inks[i].rootDoc).color !== Document(inks[0].rootDoc).color) {
-                this._noLine = false;
-                this._solidLine = false;
-                this._dashLine = false;
-            }
-            if (Document(inks[i].rootDoc).fillColor !== Document(inks[0].rootDoc).fillColor) {
-                this._solidFill = false;
-                this._noFill = false;
-            }
-            if (Document(inks[i].rootDoc).arrowStart !== Document(inks[0].rootDoc).arrowStart) {
-                this._arrowHead = false;
-            }
-            if (Document(inks[i].rootDoc).arrowEnd !== Document(inks[0].rootDoc).arrowEnd) {
-                this._arrowEnd = false;
-            }
-            if (Document(inks[i].rootDoc).x !== Document(inks[0].rootDoc).x) {
-                this._currPositionHorizontal = "";
-            }
-            if (Document(inks[i].rootDoc).y !== Document(inks[0].rootDoc).y) {
-                this._currPositionVertical = "";
-            }
-            if (Document(inks[i].rootDoc)._width !== Document(inks[0].rootDoc)._width) {
-                this._currSizeWidth = "";
-            }
-            if (Document(inks[i].rootDoc)._height !== Document(inks[0].rootDoc)._height) {
-                this._currSizeHeight = "";
-            }
-            if (Document(inks[i].rootDoc).rotation !== Document(inks[0].rootDoc).rotation) {
-                this._currRotation = "";
-            }
-        }
-    }
-
-
     @action
     upDownButtons = (dirs: string, field: string) => {
-        SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
-            const doc = Document(element.rootDoc);
-            if (doc.type === DocumentType.INK) {
-                switch (field) {
-                    case "width":
-                        if (doc.strokeWidth) {
-                            doc.strokeWidth = dirs === "up" ? doc.strokeWidth + 1 : doc.strokeWidth - 1;
-                            SetActiveInkWidth(String(doc.strokeWidth));
+        switch (field) {
+            case "horizontal": this.inks?.forEach(i => i.rootDoc.x = NumCast(i.rootDoc.x) + (dirs === "up" ? 10 : -10)); break;
+            case "vertical": this.inks?.forEach(i => i.rootDoc.y = NumCast(i.rootDoc.y) + (dirs === "up" ? 10 : -10)); break;
+            case "rotation": this.rotate((dirs === "up" ? .1 : -.1)); break;
+            case "width": this.inks?.forEach(i => i.rootDoc.strokeWidth = NumCast(i.rootDoc.strokeWidth) + (dirs === "up" ? .1 : -.1)); break;
+            case "sizeWidth":
+                this.inks?.forEach(i => {
+                    const doc = i.rootDoc;
+                    if (doc._width && doc._height) {
+                        const oldWidth = NumCast(doc._width);
+                        const oldHeight = NumCast(doc._height);
+                        doc._width = NumCast(doc._width) + (dirs === "up" ? 10 : - 10);
+                        if (this._lock) {
+                            doc._height = (NumCast(doc._width) * oldHeight) / oldWidth;
                         }
-                        break;
-                    case "sizeWidth":
-                        if (doc._width && doc._height) {
-                            const oldWidth = doc._width;
-                            const oldHeight = doc._height;
-                            doc._width = dirs === "up" ? doc._width + 10 : doc._width - 10;
-                            if (this._lock) {
-                                doc._height = (doc._width * oldHeight) / oldWidth;
-                            }
-                        }
-                        break;
-                    case "sizeHeight":
-                        if (doc._width && doc._height) {
-                            const oldWidth = doc._width;
-                            const oldHeight = doc._height;
-                            doc._height = dirs === "up" ? doc._height + 10 : doc._height - 10;
-                            if (this._lock) {
-                                doc._width = (doc._height * oldWidth) / oldHeight;
-                            }
-                        }
-                        break;
-                    case "horizontal":
-                        if (doc.x) {
-                            doc.x = dirs === "up" ? doc.x + 10 : doc.x - 10;
-                        }
-                        break;
-                    case "vertical":
-                        if (doc.y) {
-                            doc.y = dirs === "up" ? doc.y + 10 : doc.y - 10;
-                        }
-                    case "rotation":
-                        this.rotate(dirs === "up" ? Number(doc.rotation) + Number(0.1) : Number(doc.rotation) - Number(0.1));
-                        break;
-                    default:
-                        break;
-                }
-                this.selected();
-            }
-        }));
-    }
-
-
-    @action
-    editProperties = (value: any, field: string) => {
-        SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
-            const doc = Document(element.rootDoc);
-            if (doc.type === DocumentType.INK) {
-                switch (field) {
-                    case "width":
-                        SetActiveInkWidth(value);
-                        doc.strokeWidth = Number(value);
-                        break;
-                    case "color":
-                        doc.color = String(value);
-                        break;
-                    case "fill":
-                        doc.fillColor = String(value);
-                        break;
-                    case "bezier":
-                        break;
-                    case "arrowStart":
-                        doc.arrowStart = String(value);
-                        break;
-                    case "arrowEnd":
-                        doc.arrowEnd = String(value);
-                        break;
-                    case "dash":
-                        doc.dash = String(value);
-                        break;
-                    case "widthSize":
-                        if (doc._width && doc._height) {
-                            const oldWidth = doc._width;
-                            const oldHeight = doc._height;
-                            doc._width = Number(value);
-                            if (this._lock) {
-                                doc._height = (doc._width * oldHeight) / oldWidth;
-                            }
-                        }
-                        break;
-                    case "heightSize":
-                        if (doc._width && doc._height) {
-                            const oldWidth = doc._width;
-                            const oldHeight = doc._height;
-                            doc._height = Number(value);
-                            if (this._lock) {
-                                doc._width = (doc._height * oldWidth) / oldHeight;
-                            }
+                    }
+                });
+                break;
+            case "sizeHeight":
+                this.inks?.forEach(i => {
+                    const doc = i.rootDoc;
+                    if (doc._width && doc._height) {
+                        const oldWidth = NumCast(doc._width);
+                        const oldHeight = NumCast(doc._height)
+                        doc._height = NumCast(doc._height) + (dirs === "up" ? 10 : - 10);
+                        if (this._lock) {
+                            doc._width = (NumCast(doc._height) * oldWidth) / oldHeight;
                         }
-                        break;
-                    case "horizontal":
-                        doc.x = Number(value);
-                        break;
-                    case "vertical":
-                        doc.y = Number(value);
-                        break;
-                    default:
-                        break;
-                }
-            }
-            this.selected();
-        }));
-        this.checkSame();
-
+                    }
+                });
+                break;
+        }
     }
 
     @computed get close() {
@@ -294,69 +241,6 @@ export default class FormatShapePane extends AntimodeMenu {
         return modes;
     }
 
-    //detects currently selected document and change value in pane
-    @action
-    selected = () => {
-        this._selectDoc = SelectionManager.SelectedDocuments();
-        if (this._selectDoc.length === 1 && Document(this._selectDoc[0].rootDoc).type === DocumentType.INK) {
-            this._single = true;
-            const doc = Document(this._selectDoc[0].rootDoc);
-            if (doc.type === DocumentType.INK) {
-                if (doc.fillColor === "none") {
-                    this._noFill = true;
-                    this._solidFill = false;
-                } else {
-                    this._solidFill = true;
-                    this._noFill = false;
-                    this._currFill = String(doc.fillColor);
-                }
-                if (doc.color === "none") {
-                    this._noLine = true;
-                    this._solidLine = false;
-                    this._dashLine = false;
-                } else {
-                    this._currWidth = String(doc.strokeWidth);
-                    this._currColor = String(doc.color);
-                    if (doc.dash === "0") {
-                        this._solidLine = true;
-                        this._noLine = false;
-                        this._dashLine = false;
-                    } else {
-                        this._dashLine = true;
-                        this._noLine = false;
-                        this._solidLine = false;
-                    }
-
-                    this._arrowHead = doc.arrowStart === "none" ? false : true;
-                    this._arrowEnd = doc.arrowEnd === "none" ? false : true;
-                    this._currPositionHorizontal = String(doc.x);
-                    this._currPositionVertical = String(doc.y);
-                    this._currRotation = String(doc.rotation);
-                    this._currSizeHeight = String(doc._height);
-                    this._currSizeWidth = String(doc._width);
-                }
-
-            }
-        } else {
-            this._noFill = false;
-            this._solidFill = false;
-            this._single = false;
-            this._currFill = "#D0021B";
-            this._noLine = false;
-            this._solidLine = false;
-            this._dashLine = false;
-            this._currColor = "#D0021B";
-            this._arrowHead = false;
-            this._arrowEnd = false;
-            this._currPositionHorizontal = "";
-            this._currPositionVertical = "";
-            this._currRotation = "";
-            this._currSizeHeight = "";
-            this._currSizeWidth = "";
-        }
-        this.checkSame();
-    }
-
     @action
     rotate = (degrees: number) => {
         SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
@@ -395,143 +279,80 @@ export default class FormatShapePane extends AntimodeMenu {
         }));
     }
 
-    @action
-    toggle = (check: string) => {
-        switch (check) {
-            case "noFill":
-                if (!this._noFill) {
-                    this._noFill = true;
-                    this._solidFill = false;
-                }
-                break;
-            case "solidFill":
-                if (!this._solidFill) {
-                    this._solidFill = true;
-                    this._noFill = false;
-                    if (this._currFill === "none") {
-                        this._currFill = "#D0021B";
-                    }
-                }
-                break;
-            case "noLine":
-                if (!this._noLine) {
-                    this._noLine = true;
-                    this._solidLine = false;
-                    this._dashLine = false;
-                }
-                break;
-            case "solidLine":
-                if (!this._solidLine) {
-                    this._solidLine = true;
-                    this._noLine = false;
-                    this._dashLine = false;
-                    if (this._currColor === "none") {
-                        this._currColor = "#D0021B";
-                    }
-                }
-                break;
-            case "dashLine":
-                if (!this._dashLine) {
-                    this._dashLine = true;
-                    this._solidLine = false;
-                    this._noLine = false;
-                    if (this._currColor === "none") {
-                        this._currColor = "#D0021B";
-                    }
-                }
-                break;
-            case "lock":
-                if (this._lock) {
-                    this._lock = false;
-                } else {
-                    this._lock = true;
-                }
-                break;
-            case "arrowHead":
-                this._arrowHead = this._arrowHead ? false : true;
-                break;
-            case "arrowEnd":
-                this._arrowEnd = this._arrowEnd ? false : true;
-                break;
-            default:
-                break;
-        }
-    }
-
     @computed get subMenu() {
         const fillCheck = <div style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
-            <input id="nofill" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._noFill} onChange={action(() => { this.toggle("noFill"); this.editProperties("none", "fill"); })} />
+            <input id="nofill" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._noFill} onChange={action(() => this._noFill = true)} />
             No Fill
-            <br></br>
-            <input id="solidfill" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._solidFill} onChange={action(() => { this.toggle("solidFill"); this.editProperties(this._currFill, "fill"); })} />
+            <br />
+            <input id="solidfill" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._solidFill} onChange={action(() => this._solidFill = true)} />
             Solid Fill
-            <br></br>
-            <br></br>
+            <br />
+            <br />
             {this._solidFill ? "Color" : ""}
             {this._solidFill ? this.fillButton : ""}
             {this._fillBtn && this._solidFill ? this.fillPicker : ""}
 
         </div>;
-        const arrows = <> <input id="arrowHead" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._arrowHead} onChange={action(() => { this.toggle("arrowHead"); this.editProperties(this._arrowHead ? "arrowHead" : "none", "arrowStart"); })} />
+        const arrows = <> <input id="arrowStart" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._arrowStart !== ""} onChange={action(() => this._arrowStart = this._arrowStart ? "" : "arrow")} />
          Arrow Head
-            <br></br>
+            <br />
 
-            <input id="arrowEnd" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._arrowEnd} onChange={action(() => { this.toggle("arrowEnd"); this.editProperties(this._arrowEnd ? "arrowEnd" : "none", "arrowEnd"); })} />
+            <input id="arrowEnd" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._arrowEnd !== ""} onChange={action(() => this._arrowEnd = this._arrowEnd ? "" : "arrow")} />
          Arrow End
-            <br></br></>;
+            <br /></>;
         const lineCheck = <div style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
-            <input id="noLine" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._noLine} onChange={action(() => { this.toggle("noLine"); this.editProperties("none", "color"); })} />
+            <input id="noLine" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._noLine} onChange={action(() => this._noLine = true)} />
                 No Line
-                <br></br>
-            <input id="solidLine" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._solidLine} onChange={action(() => { this.toggle("solidLine"); this.editProperties(this._currColor, "color"); this.editProperties("0", "dash"); })} />
+            <br />
+            <input id="solidLine" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._solidLine} onChange={action(() => this._solidLine = true)} />
                 Solid Line
-                <br></br>
-            <input id="dashLine" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._dashLine} onChange={action(() => { this.toggle("dashLine"); this.editProperties(this._currColor, "color"); this.editProperties("2", "dash"); })} />
+            <br />
+            <input id="dashLine" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._dashLine ? true : false} onChange={action(() => this._dashLine = "2")} />
                 Dash Line
-                <br></br>
-            <br></br>
+            <br />
+            <br />
             {(this._solidLine || this._dashLine) ? "Color" : ""}
             {(this._solidLine || this._dashLine) ? this.lineButton : ""}
             {this._lineBtn && (this._solidLine || this._dashLine) ? this.linePicker : ""}
-            <br></br>
+            <br />
             {(this._solidLine || this._dashLine) ? "Width" : ""}
             {(this._solidLine || this._dashLine) ? this.widthInput : ""}
-            <br></br>
-            <br></br>
+            <br />
+            <br />
             {(this._solidLine || this._dashLine) ? arrows : ""}
 
         </div>;
 
         const sizeCheck = <div style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
             Height {this.sizeHeightInput}
-            <br></br>
-            <br></br>
+            <br />
+            <br />
 
 
             Width {this.sizeWidthInput}
-            <br></br>
-            <br></br>
+            <br />
+            <br />
 
-            <input id="lock" style={{ width: "inherit", position: "absolute", right: 0 }} type="checkbox" checked={this._lock} onChange={action(() => { this.toggle("lock"); })} />
+            <input id="lock" style={{ width: "inherit", position: "absolute", right: 0 }} type="checkbox" checked={this._lock} onChange={action(() => this._lock = !this._lock)} />
                 Lock Ratio
-            <br></br>
-            <br></br>
+            <br />
+            <br />
 
 
             Rotation {this.rotationInput}
-            <br></br>
-            <br></br>
+            <br />
+            <br />
 
 
         </div>;
         const positionCheck = <div style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
             Horizontal {this.positionHorizontalInput}
-            <br></br>
-            <br></br>
+            <br />
+            <br />
 
             Vertical {this.positionVerticalInput}
-            <br></br>
-            <br></br>
+            <br />
+            <br />
 
 
         </div>;
@@ -574,29 +395,30 @@ export default class FormatShapePane extends AntimodeMenu {
     }
 
     @computed get fillButton() {
-        const fillButton = <><button
-            className="antimodeMenu-button"
-            key="fill"
-            title="fillChanger"
-            onPointerDown={action(e => this._fillBtn = !this._fillBtn)}
-            style={{
-                // backgroundColor: "121212",
-                position: "absolute", right: 80
-            }}>
-            <FontAwesomeIcon icon="fill-drip" size="lg" />
-            <div className="color-previewI" style={{ backgroundColor: this._currFill ?? "121212" }}></div>
-        </button>
+        return <>
+            <button
+                className="antimodeMenu-button"
+                key="fill"
+                title="fillChanger"
+                onPointerDown={action(e => this._fillBtn = !this._fillBtn)}
+                style={{
+                    // backgroundColor: "121212",
+                    position: "absolute", right: 80
+                }}>
+                <FontAwesomeIcon icon="fill-drip" size="lg" />
+                <div className="color-previewI" style={{ backgroundColor: this._currFill ?? "121212" }}></div>
+            </button>
             <br></br>
-            <br></br></>;
-        return fillButton;
+            <br></br>
+        </>;
     }
     @computed get fillPicker() {
-        const fillPicker = <div className="btn-group-palette" key="fill" >
+        return <div className="btn-group-palette" key="fill" >
             {this._palette.map(color => {
                 return <button
                     className="antimodeMenu-button"
                     key={color}
-                    onPointerDown={action(() => { this._currFill = color; this.editProperties(color, "fill"); })}
+                    onPointerDown={action(() => this._currFill = color)}
                     style={{
                         // backgroundColor: this._fillBtn ? "121212" : "",
                         zIndex: 1001
@@ -606,33 +428,33 @@ export default class FormatShapePane extends AntimodeMenu {
             })}
 
         </div>;
-        return fillPicker;
     }
 
     @computed get lineButton() {
-        const lineButton = <><button
-            className="antimodeMenu-button"
-            key="line"
-            title="lineChanger"
-            onPointerDown={action(e => this._lineBtn = !this._lineBtn)}
-            style={{
-                // backgroundColor: "121212",
-                position: "absolute", right: 80
-            }}>
-            <FontAwesomeIcon icon="pen-nib" size="lg" />
-            <div className="color-previewI" style={{ backgroundColor: this._currColor ?? "121212" }}></div>
-        </button>
-            <br></br>
-            <br></br></>;
-        return lineButton;
+        return <>
+            <button
+                className="antimodeMenu-button"
+                key="line"
+                title="lineChanger"
+                onPointerDown={action(e => this._lineBtn = !this._lineBtn)}
+                style={{
+                    // backgroundColor: "121212",
+                    position: "absolute", right: 80
+                }}>
+                <FontAwesomeIcon icon="pen-nib" size="lg" />
+                <div className="color-previewI" style={{ backgroundColor: this._currColor ?? "121212" }}></div>
+            </button>
+            <br />
+            <br />
+        </>;
     }
     @computed get linePicker() {
-        const linePicker = <div className="btn-group-palette" key="line" >
+        return <div className="btn-group-palette" key="line" >
             {this._palette.map(color => {
                 return <button
                     className="antimodeMenu-button"
                     key={color}
-                    onPointerDown={action(() => { this._currColor = color; this.editProperties(color, "color"); })}
+                    onPointerDown={action(() => this._currColor = color)}
                     style={{
                         // backgroundColor: this._lineBtn ? "121212" : "",
                         zIndex: 1001
@@ -642,19 +464,19 @@ export default class FormatShapePane extends AntimodeMenu {
             })}
 
         </div>;
-        return linePicker;
     }
     @computed get widthInput() {
         const widthInput = <>
             <input
                 style={{ color: "black", width: 80, position: "absolute", right: 20 }}
-                type="text" value={this._currWidth}
-                onChange={e => this.onChange(e.target.value, "width")}
-                autoFocus></input>  <button
-                    className="antiMenu-Buttonup"
-                    key="up"
-                    onPointerDown={action(() => { this.upDownButtons("up", "width"); })}
-                    style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0 }}>
+                type="text" value={this._currStrokeWidth}
+                onChange={e => this._currStrokeWidth = e.target.value}
+                autoFocus />
+            <button
+                className="antiMenu-Buttonup"
+                key="up"
+                onPointerDown={action(() => { this.upDownButtons("up", "width"); })}
+                style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0 }}>
                 ˄
             </button>
             <br />
@@ -672,8 +494,8 @@ export default class FormatShapePane extends AntimodeMenu {
             <input
                 style={{ color: "black", width: 80, position: "absolute", right: 20 }}
                 type="text" value={this._currSizeHeight}
-                onChange={e => this.onChange(e.target.value, "sizeHeight")}
-                autoFocus></input>
+                onChange={e => this._currSizeHeight = e.target.value}
+                autoFocus />
             <button
                 className="antiMenu-Buttonup"
                 key="up"
@@ -698,8 +520,8 @@ export default class FormatShapePane extends AntimodeMenu {
             <input
                 style={{ color: "black", width: 80, position: "absolute", right: 20 }}
                 type="text" value={this._currSizeWidth}
-                onChange={e => this.onChange(e.target.value, "sizeWidth")}
-                autoFocus></input>
+                onChange={e => this._currSizeWidth = e.target.value}
+                autoFocus />
             <button
                 className="antiMenu-Buttonup"
                 key="up"
@@ -707,7 +529,7 @@ export default class FormatShapePane extends AntimodeMenu {
                 style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0 }}>
                 ˄
             </button>
-            <br></br>
+            <br />
             <button
                 className="antiMenu-Buttonup"
                 key="up"
@@ -724,7 +546,7 @@ export default class FormatShapePane extends AntimodeMenu {
                 <input
                     style={{ color: "black", width: 80, position: "absolute", right: 20 }}
                     type="text" value={this._currRotation}
-                    onChange={e => this.onChange(e.target.value, "rotation")}
+                    onChange={e => this._currRotation = e.target.value}
                     autoFocus></input>
                 <button
                     className="antiMenu-Buttonup"
@@ -733,7 +555,7 @@ export default class FormatShapePane extends AntimodeMenu {
                     style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0 }}>
                     ˄
             </button>
-                <br></br>
+                <br />
                 <button
                     className="antiMenu-Buttonup"
                     key="up"
@@ -745,137 +567,56 @@ export default class FormatShapePane extends AntimodeMenu {
     }
 
     @computed get positionHorizontalInput() {
-        const positionHorizontalInput =
-            <><input
+        return <>
+            <input
                 style={{ color: "black", width: 80, position: "absolute", right: 20 }}
                 type="text" value={this._currPositionHorizontal}
-                onChange={e => this.onChange(e.target.value, "positionHorizontal")}
-                autoFocus></input>
-                <button
-                    className="antiMenu-Buttonup"
-                    key="up"
-                    onPointerDown={action(() => { this.upDownButtons("up", "horizontal"); })}
-                    style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0 }}>
-                    ˄
+                onChange={e => this._currPositionHorizontal = e.target.value}
+                autoFocus
+            />
+            <button
+                className="antiMenu-Buttonup"
+                key="up"
+                onPointerDown={action(() => { this.upDownButtons("up", "horizontal"); })}
+                style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0 }}>
+                ˄
+                </button>
+            <br></br>
+            <button
+                className="antiMenu-Buttonup"
+                key="up"
+                onPointerDown={action(() => { this.upDownButtons("down", "horizontal"); })}
+                style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0, marginTop: -8 }}>
+                ˅
             </button>
-                <br></br>
-                <button
-                    className="antiMenu-Buttonup"
-                    key="up"
-                    onPointerDown={action(() => { this.upDownButtons("down", "horizontal"); })}
-                    style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0, marginTop: -8 }}>
-                    ˅
-            </button></>;
-        return positionHorizontalInput;
+        </>;
     }
 
     @computed get positionVerticalInput() {
-        const positionVerticalInput =
-            <><input
+        return <>
+            <input
                 style={{ color: "black", width: 80, position: "absolute", right: 20 }}
                 type="text" value={this._currPositionVertical}
-                onChange={e => this.onChange(e.target.value, "positionVertical")}
+                onChange={e => this._currPositionVertical = e.target.value}
                 autoFocus></input>
-                <button
-                    className="antiMenu-Buttonup"
-                    key="up"
-                    onPointerDown={action(() => { this.upDownButtons("down", "vertical"); })}
-                    style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0 }}>
-                    ˄
+            <button
+                className="antiMenu-Buttonup"
+                key="up"
+                onPointerDown={action(() => { this.upDownButtons("down", "vertical"); })}
+                style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0 }}>
+                ˄
             </button>
-                <br></br>
-                <button
-                    className="antiMenu-Buttonup"
-                    key="up"
-                    onPointerDown={action(() => { this.upDownButtons("up", "vertical"); })}
-                    style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0, marginTop: -8 }}>
-                    ˅
-            </button></>;
-        return positionVerticalInput;
-    }
-
-    //change inputs
-    @action
-    onChange = (val: string, property: string): void => {
-        if (!Number.isNaN(Number(val)) && Number(val) !== null && val !== " ") {
-
-            switch (property) {
-                case "width":
-                    this._currWidth = val;
-                    if (val !== "") {
-                        this.editProperties(this._currWidth, "width");
-                    }
-                    break;
-                case "sizeHeight":
-                    this._currSizeHeight = val;
-                    if (val !== "") {
-                        this.editProperties(this._currSizeHeight, "heightSize");
-                    }
-                    break;
-                case "sizeWidth":
-                    this._currSizeWidth = val;
-                    if (val !== "") {
-
-                        this.editProperties(this._currSizeWidth, "widthSize");
-                    }
-                    break;
-                case "rotation":
-
-                    this._currRotation = val;
-                    if (val !== "") {
-
-                        this.rotate(Number(val));
-                    }
-                    break;
-                case "positionHorizontal":
-                    this._currPositionHorizontal = val; if (val !== "") {
-
-                        this.editProperties(this._currPositionHorizontal, "horizontal");
-                    }
-
-                    break;
-                case "positionVertical":
-                    this._currPositionVertical = val;
-                    if (val !== "") {
-
-                        this.editProperties(this._currPositionVertical, "vertical");
-                    }
-
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-
-
-    @computed get widthPicker() {
-        var widthPicker = <button
-            className="antimodeMenu-button"
-            key="width"
-            onPointerDown={action(e => this._widthBtn = !this._widthBtn)}
-            style={{ backgroundColor: this._widthBtn ? "121212" : "" }}>
-            <FontAwesomeIcon icon="bars" size="lg" />
-        </button>;
-        if (this._widthBtn) {
-            widthPicker = <div className="btn2-group" key="width">
-                {widthPicker}
-                {this._width.map(wid => {
-                    return <button
-                        className="antimodeMenu-button"
-                        key={wid}
-                        onPointerDown={action(() => { SetActiveInkWidth(wid); this._widthBtn = false; this.editProperties(wid, "width"); })}
-                        style={{ backgroundColor: this._widthBtn ? "121212" : "" }}>
-                        {wid}
-                    </button>;
-                })}
-            </div>;
-        }
-        return widthPicker;
+            <br></br>
+            <button
+                className="antiMenu-Buttonup"
+                key="up"
+                onPointerDown={action(() => { this.upDownButtons("up", "vertical"); })}
+                style={{ position: "absolute", width: 20, height: 10, right: 0, padding: 0, marginTop: -8 }}>
+                ˅
+            </button>
+        </>;
     }
 
-
-
     render() {
         const buttons = [
 
diff --git a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
index e4f3248d0..0866db2be 100644
--- a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
@@ -25,19 +25,19 @@ library.add(faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSup
 export default class InkOptionsMenu extends AntimodeMenu {
     static Instance: InkOptionsMenu;
 
-    private _palette = ["#D0021B", "#F5A623", "#F8E71C", "#8B572A", "#7ED321", "#417505", "#9013FE", "#4A90E2", "#50E3C2", "#B8E986", "#000000", "#4A4A4A", "#9B9B9B", "#FFFFFF", "none"];
+    private _palette = ["#D0021B", "#F5A623", "#F8E71C", "#8B572A", "#7ED321", "#417505", "#9013FE", "#4A90E2", "#50E3C2", "#B8E986", "#000000", "#4A4A4A", "#9B9B9B", "#FFFFFF", ""];
     private _width = ["1", "5", "10", "100"];
     // private _buttons = ["circle", "triangle", "rectangle", "arrow", "line"];
     // private _icons = ["O", "∆", "ロ", "➜", "-"];
     // private _buttons = ["circle", "triangle", "rectangle", "line", "noRec", "",];
     // private _icons = ["O", "∆", "ロ", "⎯⎯⎯", "✖︎", " "];
     //arrowStart and arrowEnd must match and defs must exist in Inking Stroke
-    // private _arrowStart = ["arrowHead", "arrowHead", "dot", "dot", "none"];
+    // private _arrowStart = ["arrowStart", "arrowStart", "dot", "dot", "none"];
     // private _arrowEnd = ["none", "arrowEnd", "none", "dot", "none"];
     // private _arrowIcons = ["→", "↔︎", "•", "••", " "];
     private _draw = ["⎯", "→", "↔︎", "∿", "↝", "↭", "ロ", "O", "∆"];
-    private _head = ["none", "none", "arrowHead", "none", "none", "arrowHead", "none", "none", "none"];
-    private _end = ["none", "arrowEnd", "arrowEnd", "none", "arrowEnd", "arrowEnd", "none", "none", "none"];
+    private _head = ["", "", "arrow", "", "", "arrow", "", "", ""];
+    private _end = ["", "arrow", "arrow", "", "arrow", "arrow", "", "", ""];
     private _shape = ["", "", "", "", "", "", "rectangle", "circle", "triangle"];
 
     @observable _shapesNum = this._shape.length;
@@ -122,12 +122,6 @@ export default class InkOptionsMenu extends AntimodeMenu {
                     case "bezier":
                         // doc.strokeBezier === 300 ? doc.strokeBezier = 0 : doc.strokeBezier = 300;
                         break;
-                    case "arrowStart":
-                        doc.arrowStart = String(value);
-                        break;
-                    case "arrowEnd":
-                        doc.arrowEnd = String(value);
-                        break;
                     case "dash":
                         doc.dash = Number(value);
                     default:
@@ -165,13 +159,12 @@ export default class InkOptionsMenu extends AntimodeMenu {
                             SetActiveArrowEnd(this._end[i]);
                             SetActiveBezierApprox("300");
 
-                            //  this.editProperties(this._head[i], "arrowStart"), this.editProperties(this._end[i], "arrowEnd"); 
                             GestureOverlay.Instance.InkShape = this._shape[i];
                         } else {
                             this._selected = this._shapesNum;
                             Doc.SetSelectedTool(InkTool.None);
-                            SetActiveArrowStart("none");
-                            SetActiveArrowEnd("none");
+                            SetActiveArrowStart("");
+                            SetActiveArrowEnd("");
                             GestureOverlay.Instance.InkShape = "";
                             SetActiveBezierApprox("0");
 
@@ -189,13 +182,12 @@ export default class InkOptionsMenu extends AntimodeMenu {
                             SetActiveArrowEnd(this._end[i]);
                             SetActiveBezierApprox("300");
 
-                            //  this.editProperties(this._head[i], "arrowStart"), this.editProperties(this._end[i], "arrowEnd"); 
                             GestureOverlay.Instance.InkShape = this._shape[i];
                         } else {
                             this._selected = this._shapesNum;
                             Doc.SetSelectedTool(InkTool.None);
-                            SetActiveArrowStart("none");
-                            SetActiveArrowEnd("none");
+                            SetActiveArrowStart("");
+                            SetActiveArrowEnd("");
                             GestureOverlay.Instance.InkShape = "";
                             SetActiveBezierApprox("0");
 
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 137b387c0..31c5a8b13 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -14,7 +14,6 @@ import { ViewBoxBaseComponent } from "../DocComponent";
 import { ActiveInkPen, ActiveInkWidth, ActiveInkBezierApprox, SetActiveInkColor, SetActiveInkWidth, SetActiveBezierApprox } from "../InkingStroke";
 import "./ColorBox.scss";
 import { FieldView, FieldViewProps } from './FieldView';
-import { FormattedTextBox } from "./formattedText/FormattedTextBox";
 
 type ColorDocument = makeInterface<[typeof documentSchema]>;
 const ColorDocument = makeInterface(documentSchema);
-- 
cgit v1.2.3-70-g09d2


From cef8f8d5fb9f5b9dc1f56768c4fa5ec406fbc6ba Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 11 Jul 2020 20:02:55 -0400
Subject: fixed layout of inkOptionsMenu

---
 src/client/views/collections/collectionFreeForm/FormatShapePane.scss | 2 +-
 src/client/views/collections/collectionFreeForm/FormatShapePane.tsx  | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/FormatShapePane.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.scss b/src/client/views/collections/collectionFreeForm/FormatShapePane.scss
index 80a74fe4c..7720140b9 100644
--- a/src/client/views/collections/collectionFreeForm/FormatShapePane.scss
+++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.scss
@@ -1,6 +1,6 @@
 .antimodeMenu-button {
     width: 200px;
-    position: absolute;
+    position: relative;
     text-align: left;
 
     .color-previewI {
diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
index 3a1c8b18d..095473330 100644
--- a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
+++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
@@ -136,7 +136,7 @@ export default class FormatShapePane extends AntimodeMenu {
     }
 
     @computed get close() {
-        return <button className="antimodeMenu-button" key="close" onPointerDown={action(() => this.closePane())} style={{ right: 0 }}>
+        return <button className="antimodeMenu-button" key="close" onPointerDown={action(() => this.closePane())} style={{ position: "absolute", right: 0 }}>
             X
         </button>;
     }
@@ -320,7 +320,7 @@ export default class FormatShapePane extends AntimodeMenu {
 
     colorButton(value: string, setter: () => {}) {
         return <>
-            <button className="antimodeMenu-button" key="fill" onPointerDown={action(e => setter())} style={{ right: 80 }}>
+            <button className="antimodeMenu-button" key="fill" onPointerDown={action(e => setter())} style={{ position: "absolute", right: 80 }}>
                 <FontAwesomeIcon icon="fill-drip" size="lg" />
                 <div className="color-previewI" style={{ backgroundColor: value ?? "121212" }} />
             </button>
-- 
cgit v1.2.3-70-g09d2


From 49b6ab8536f570ef244199ac39194d3b176c9e77 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 11 Jul 2020 20:47:55 -0400
Subject: added open formatting pane to inkOptions.  switched to radio buttons
 for some options.  changed names of stroke parameters to start with 'stroke"

---
 src/client/views/InkingStroke.tsx                  |  4 ++--
 .../collectionFreeForm/FormatShapePane.tsx         | 25 +++++++++++-----------
 .../collectionFreeForm/InkOptionsMenu.tsx          | 17 +++++++++++----
 src/client/views/nodes/ColorBox.tsx                | 11 ++++++++--
 4 files changed, 37 insertions(+), 20 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/FormatShapePane.tsx')

diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 46f714875..d7f956e4f 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -63,8 +63,8 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         const strokeColor = StrCast(this.layoutDoc.color, "");
         const points = InteractionUtils.CreatePolyline(data, left, top, strokeColor, strokeWidth, strokeWidth,
             StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"),
-            StrCast(this.layoutDoc.arrowStart), StrCast(this.layoutDoc.arrowEnd),
-            StrCast(this.layoutDoc.dash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5, false);
+            StrCast(this.layoutDoc.strokeArrowStart), StrCast(this.layoutDoc.strokeArrowEnd),
+            StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5, false);
         const hpoints = InteractionUtils.CreatePolyline(data, left, top,
             this.props.isSelected() && strokeWidth > 5 ? strokeColor : "transparent", strokeWidth, (strokeWidth + 15),
             StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"),
diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
index 095473330..77d1b646d 100644
--- a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
+++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
@@ -43,10 +43,10 @@ export default class FormatShapePane extends AntimodeMenu {
     @computed get _noFill() { return this.inks?.reduce((p, i) => p && !i.rootDoc.fillColor ? true : false, true) || false; }
     @computed get _solidFill() { return this.inks?.reduce((p, i) => p && i.rootDoc.fillColor ? true : false, true) || false; }
     @computed get _noLine() { return this.inks?.reduce((p, i) => p && !i.rootDoc.color ? true : false, true) || false; }
-    @computed get _solidLine() { return this.inks?.reduce((p, i) => p && i.rootDoc.color && (!i.rootDoc.dash || i.rootDoc.dash === "0") ? true : false, true) || false; }
-    @computed get _arrowStart() { return this.getField("arrowStart") || ""; }
-    @computed get _arrowEnd() { return this.getField("arrowEnd") || ""; }
-    @computed get _dashLine() { return !this._noLine && this.getField("dash") || ""; }
+    @computed get _solidLine() { return this.inks?.reduce((p, i) => p && i.rootDoc.color && (!i.rootDoc.strokeDash || i.rootDoc.strokeDash === "0") ? true : false, true) || false; }
+    @computed get _arrowStart() { return this.getField("strokeArrowStart") || ""; }
+    @computed get _arrowEnd() { return this.getField("strokeArrowEnd") || ""; }
+    @computed get _dashLine() { return !this._noLine && this.getField("strokeDash") || ""; }
     @computed get _currSizeHeight() { return this.getField("_height"); }
     @computed get _currSizeWidth() { return this.getField("_width"); }
     @computed get _currRotation() { return this.getField("rotation"); }
@@ -59,13 +59,13 @@ export default class FormatShapePane extends AntimodeMenu {
     set _solidFill(value) { this._noFill = !value; }
     set _currFill(value) { value && (this._lastFill = value); this.inks?.forEach(i => i.rootDoc.fillColor = value ? value : undefined); }
     set _currColor(value) { value && (this._lastLine = value); this.inks?.forEach(i => i.rootDoc.color = value ? value : undefined); }
-    set _arrowStart(value) { this.inks?.forEach(i => i.rootDoc.arrowStart = value); }
-    set _arrowEnd(value) { this.inks?.forEach(i => i.rootDoc.arrowEnd = value); }
+    set _arrowStart(value) { this.inks?.forEach(i => i.rootDoc.strokeArrowStart = value); }
+    set _arrowEnd(value) { this.inks?.forEach(i => i.rootDoc.strokeArrowEnd = value); }
     set _noLine(value) { this._currColor = value ? "" : this._lastLine; }
     set _solidLine(value) { this._dashLine = ""; this._noLine = !value; }
     set _dashLine(value) {
         value && (this._lastDash = value) && (this._noLine = false);
-        this.inks?.forEach(i => i.rootDoc.dash = value ? this._lastDash : undefined);
+        this.inks?.forEach(i => i.rootDoc.strokeDash = value ? this._lastDash : undefined);
     }
     set _currXpos(value) { this.inks?.forEach(i => i.rootDoc.x = Number(value)); }
     set _currYpos(value) { this.inks?.forEach(i => i.rootDoc.y = Number(value)); }
@@ -192,10 +192,10 @@ export default class FormatShapePane extends AntimodeMenu {
 
     @computed get subMenu() {
         const fillCheck = <div key="fill" style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
-            <input id="nofill" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._noFill} onChange={action(() => this._noFill = true)} />
+            <input id="nofill" style={{ width: "inherit", position: "absolute" }} type="radio" checked={this._noFill} onChange={action(() => this._noFill = true)} />
             No Fill
             <br />
-            <input id="solidfill" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._solidFill} onChange={action(() => this._solidFill = true)} />
+            <input id="solidfill" style={{ width: "inherit", position: "absolute" }} type="radio" checked={this._solidFill} onChange={action(() => this._solidFill = true)} />
             Solid Fill
             <br />
             <br />
@@ -214,13 +214,13 @@ export default class FormatShapePane extends AntimodeMenu {
         </>;
 
         const lineCheck = <div key="lineCheck" style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
-            <input id="noLine" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._noLine} onChange={action(() => this._noLine = true)} />
+            <input id="noLine" style={{ width: "inherit", position: "absolute" }} type="radio" checked={this._noLine} onChange={action(() => this._noLine = true)} />
                 No Line
             <br />
-            <input id="solidLine" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._solidLine} onChange={action(() => this._solidLine = true)} />
+            <input id="solidLine" style={{ width: "inherit", position: "absolute" }} type="radio" checked={this._solidLine} onChange={action(() => this._solidLine = true)} />
                 Solid Line
             <br />
-            <input id="dashLine" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._dashLine ? true : false} onChange={action(() => this._dashLine = "2")} />
+            <input id="dashLine" style={{ width: "inherit", position: "absolute" }} type="radio" checked={this._dashLine ? true : false} onChange={action(() => this._dashLine = "2")} />
                 Dash Line
             <br />
             <br />
@@ -230,6 +230,7 @@ export default class FormatShapePane extends AntimodeMenu {
             <br />
             {(this._solidLine || this._dashLine) ? "Width" : ""}
             {(this._solidLine || this._dashLine) ? this.widthInput : ""}
+            {(this._solidLine || this._dashLine) ? <input type="range" defaultValue={Number(this._currStrokeWidth || "1")} min={1} max={100} onChange={e => this._currStrokeWidth = e.target.value} /> : (null)}
             <br />
             <br />
             {(this._solidLine || this._dashLine) ? arrows : ""}
diff --git a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
index ddba8a66d..7f0bb5364 100644
--- a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
@@ -18,6 +18,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
 import { faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSubscript, faSuperscript, faIndent, faEyeDropper, faCaretDown, faPalette, faArrowsAlt, faHighlighter, faLink, faPaintRoller, faSleigh, faBars, faFillDrip, faBrush, faPenNib, faShapes, faArrowLeft, faEllipsisH, faBezierCurve, } from "@fortawesome/free-solid-svg-icons";
 import { Cast, StrCast, BoolCast } from "../../../../fields/Types";
+import FormatShapePane from "./FormatShapePane";
 
 library.add(faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, faCaretDown, faPalette, faArrowsAlt, faHighlighter, faLink, faPaintRoller, faBars, faFillDrip, faBrush, faPenNib, faShapes, faArrowLeft, faEllipsisH, faBezierCurve);
 
@@ -40,7 +41,7 @@ export default class InkOptionsMenu extends AntimodeMenu {
     private _draw = ["⎯", "→", "↔︎", "∿", "↝", "↭", "ロ", "O", "∆"];
     private _head = ["", "", "arrow", "", "", "arrow", "", "", ""];
     private _end = ["", "arrow", "arrow", "", "arrow", "arrow", "", "", ""];
-    private _shape = ["", "", "", "", "", "", "rectangle", "circle", "triangle"];
+    private _shape = ["line", "line", "line", "", "", "", "rectangle", "circle", "triangle"];
 
     @observable _shapesNum = this._shape.length;
     @observable _selected = this._shapesNum;
@@ -127,7 +128,7 @@ export default class InkOptionsMenu extends AntimodeMenu {
                         // doc.strokeBezier === 300 ? doc.strokeBezier = 0 : doc.strokeBezier = 300;
                         break;
                     case "dash":
-                        doc.dash = Number(value);
+                        doc.strokeDash = Number(value);
                     default:
                         break;
                 }
@@ -144,7 +145,7 @@ export default class InkOptionsMenu extends AntimodeMenu {
     @action
     changeDash = (e: React.PointerEvent): void => {
         SetActiveDash(ActiveDash() === "0" ? "2" : "0");
-        this.editProperties(ActiveDash(), "dash");
+        this.editProperties(ActiveDash(), "strokeDash");
     }
 
     @computed get drawButtons() {
@@ -296,6 +297,14 @@ export default class InkOptionsMenu extends AntimodeMenu {
         }
         return colorPicker;
     }
+    @computed get formatPane() {
+        return <button className="antimodeMenu-button" key="format"
+            title="toggle foramatting pane"
+            onPointerDown={action(e => FormatShapePane.Instance.Pinned = !FormatShapePane.Instance.Pinned)}
+            style={{ backgroundColor: this._fillBtn ? "121212" : "" }}>
+            <FontAwesomeIcon icon="chevron-right" size="lg" />
+        </button>;
+    }
 
     @computed get fillPicker() {
         var fillPicker = <button
@@ -403,12 +412,12 @@ export default class InkOptionsMenu extends AntimodeMenu {
             this.colorPicker,
             this.fillPicker,
             this.drawButtons,
+            this.formatPane,
             // this.arrowPicker,
             // this.dashButton,
             <button className="antimodeMenu-button" key="pin menu" title="Pin menu" onClick={this.toggleMenuPin} style={{ backgroundColor: this.Pinned ? "#121212" : "", display: this.collapsed ? "none" : undefined }}>
                 <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.Pinned ? 45 : 0}deg)` }} />
             </button>
-
         ];
 
         // return this.getElement(buttons);
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index c35c52644..57028b0ca 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -14,6 +14,7 @@ import { ViewBoxBaseComponent } from "../DocComponent";
 import { ActiveInkPen, ActiveInkWidth, ActiveInkBezierApprox, SetActiveInkColor, SetActiveInkWidth, SetActiveBezierApprox } from "../InkingStroke";
 import "./ColorBox.scss";
 import { FieldView, FieldViewProps } from './FieldView';
+import { DocumentType } from "../../documents/DocumentTypes";
 
 type ColorDocument = makeInterface<[typeof documentSchema]>;
 const ColorDocument = makeInterface(documentSchema);
@@ -62,9 +63,15 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument
                     StrCast(selDoc?._backgroundColor, StrCast(selDoc?.backgroundColor, "black")))} />
             <div style={{ display: "grid", gridTemplateColumns: "20% 80%", paddingTop: "10px" }}>
                 <div> {ActiveInkWidth() ?? 2}</div>
-                <input type="range" defaultValue={ActiveInkWidth() ?? 2} min={1} max={100} onChange={(e: React.ChangeEvent<HTMLInputElement>) => SetActiveInkWidth(e.target.value)} />
+                <input type="range" defaultValue={ActiveInkWidth() ?? 2} min={1} max={100} onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+                    SetActiveInkWidth(e.target.value);
+                    SelectionManager.SelectedDocuments().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeWidth = Number(e.target.value));
+                }} />
                 <div> {ActiveInkBezierApprox() ?? 2}</div>
-                <input type="range" defaultValue={ActiveInkBezierApprox() ?? 2} min={0} max={300} onChange={(e: React.ChangeEvent<HTMLInputElement>) => SetActiveBezierApprox(e.target.value)} />
+                <input type="range" defaultValue={ActiveInkBezierApprox() ?? 2} min={0} max={300} onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+                    SetActiveBezierApprox(e.target.value);
+                    SelectionManager.SelectedDocuments().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeBezier = e.target.value);
+                }} />
                 <br />
                 <br />
             </div>
-- 
cgit v1.2.3-70-g09d2


From add9dfeec764b5a60a57344c79fd23a91c031a2d Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 12 Jul 2020 10:31:06 -0400
Subject: final cleanup of names and structure of FormatShapePane

---
 src/client/views/InkingStroke.tsx                  |   2 +-
 .../collectionFreeForm/FormatShapePane.scss        |   5 +
 .../collectionFreeForm/FormatShapePane.tsx         | 404 +++++++++------------
 .../collectionFreeForm/InkOptionsMenu.tsx          |   9 +-
 4 files changed, 182 insertions(+), 238 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/FormatShapePane.tsx')

diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index d7f956e4f..abc698e62 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -63,7 +63,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         const strokeColor = StrCast(this.layoutDoc.color, "");
         const points = InteractionUtils.CreatePolyline(data, left, top, strokeColor, strokeWidth, strokeWidth,
             StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"),
-            StrCast(this.layoutDoc.strokeArrowStart), StrCast(this.layoutDoc.strokeArrowEnd),
+            StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker),
             StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5, false);
         const hpoints = InteractionUtils.CreatePolyline(data, left, top,
             this.props.isSelected() && strokeWidth > 5 ? strokeColor : "transparent", strokeWidth, (strokeWidth + 15),
diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.scss b/src/client/views/collections/collectionFreeForm/FormatShapePane.scss
index 7720140b9..88876471c 100644
--- a/src/client/views/collections/collectionFreeForm/FormatShapePane.scss
+++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.scss
@@ -22,6 +22,11 @@
     padding: 0;
 }
 
+.formatShapePane-inputBtn {
+    width: inherit;
+     position: absolute;
+}
+
 .sketch-picker {
     background: #323232;
 
diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
index 77d1b646d..56ba9e96a 100644
--- a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
+++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
@@ -1,18 +1,16 @@
 import React = require("react");
-import AntimodeMenu from "../../AntimodeMenu";
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
-import { observable, action, computed } from "mobx";
-import "./FormatShapePane.scss";
-import { Scripting } from "../../../util/Scripting";
-import { InkField } from "../../../../fields/InkField";
-import { Doc, Opt, Field } from "../../../../fields/Doc";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { DocumentView } from "../../../views/nodes/DocumentView";
+import { Doc, Field, Opt } from "../../../../fields/Doc";
 import { Document } from "../../../../fields/documentSchemas";
+import { InkField } from "../../../../fields/InkField";
+import { BoolCast, Cast, NumCast } from "../../../../fields/Types";
 import { DocumentType } from "../../../documents/DocumentTypes";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { Cast, StrCast, BoolCast, NumCast } from "../../../../fields/Types";
+import { SelectionManager } from "../../../util/SelectionManager";
+import AntimodeMenu from "../../AntimodeMenu";
+import "./FormatShapePane.scss";
 
 @observer
 export default class FormatShapePane extends AntimodeMenu {
@@ -23,63 +21,62 @@ export default class FormatShapePane extends AntimodeMenu {
     private _lastDash = "2";
     private _palette = ["#D0021B", "#F5A623", "#F8E71C", "#8B572A", "#7ED321", "#417505", "#9013FE", "#4A90E2", "#50E3C2", "#B8E986", "#000000", "#4A4A4A", "#9B9B9B", "#FFFFFF"];
     private _mode = ["fill-drip", "ruler-combined"];
-    private _subMenu = ["fill", "line", "size", "position"];
 
     @observable private _subOpen = [false, false, false, false];
-    @observable private _currMode: string = "fill-drip";
+    @observable private _currMode = "fill-drip";
     @observable private _lock = false;
     @observable private _fillBtn = false;
     @observable private _lineBtn = false;
 
     getField(key: string) {
-        return this.inks?.reduce((p, i) =>
+        return this.selectedInk?.reduce((p, i) =>
             (p === undefined || (p && p === i.rootDoc[key])) && i.rootDoc[key] !== "0" ? Field.toString(i.rootDoc[key] as Field) : "", undefined as Opt<string>)
     }
 
-    @computed get inks() {
+    @computed get selectedInk() {
         const inks = SelectionManager.SelectedDocuments().filter(i => Document(i.rootDoc).type === DocumentType.INK);
         return inks.length ? inks : undefined;
     }
-    @computed get _noFill() { return this.inks?.reduce((p, i) => p && !i.rootDoc.fillColor ? true : false, true) || false; }
-    @computed get _solidFill() { return this.inks?.reduce((p, i) => p && i.rootDoc.fillColor ? true : false, true) || false; }
-    @computed get _noLine() { return this.inks?.reduce((p, i) => p && !i.rootDoc.color ? true : false, true) || false; }
-    @computed get _solidLine() { return this.inks?.reduce((p, i) => p && i.rootDoc.color && (!i.rootDoc.strokeDash || i.rootDoc.strokeDash === "0") ? true : false, true) || false; }
-    @computed get _arrowStart() { return this.getField("strokeArrowStart") || ""; }
-    @computed get _arrowEnd() { return this.getField("strokeArrowEnd") || ""; }
-    @computed get _dashLine() { return !this._noLine && this.getField("strokeDash") || ""; }
-    @computed get _currSizeHeight() { return this.getField("_height"); }
-    @computed get _currSizeWidth() { return this.getField("_width"); }
-    @computed get _currRotation() { return this.getField("rotation"); }
-    @computed get _currXpos() { return this.getField("x"); }
-    @computed get _currYpos() { return this.getField("y"); }
-    @computed get _currStrokeWidth() { return this.getField("strokeWidth"); }
-    @computed get _currFill() { const cfill = this.getField("fillColor") || ""; cfill && (this._lastFill = cfill); return cfill; }
-    @computed get _currColor() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; }
-    set _noFill(value) { this._currFill = value ? "" : this._lastFill; }
-    set _solidFill(value) { this._noFill = !value; }
-    set _currFill(value) { value && (this._lastFill = value); this.inks?.forEach(i => i.rootDoc.fillColor = value ? value : undefined); }
-    set _currColor(value) { value && (this._lastLine = value); this.inks?.forEach(i => i.rootDoc.color = value ? value : undefined); }
-    set _arrowStart(value) { this.inks?.forEach(i => i.rootDoc.strokeArrowStart = value); }
-    set _arrowEnd(value) { this.inks?.forEach(i => i.rootDoc.strokeArrowEnd = value); }
-    set _noLine(value) { this._currColor = value ? "" : this._lastLine; }
-    set _solidLine(value) { this._dashLine = ""; this._noLine = !value; }
-    set _dashLine(value) {
-        value && (this._lastDash = value) && (this._noLine = false);
-        this.inks?.forEach(i => i.rootDoc.strokeDash = value ? this._lastDash : undefined);
+    @computed get unFilled() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.fillColor ? true : false, true) || false; }
+    @computed get unStrokd() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.color ? true : false, true) || false; }
+    @computed get solidFil() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.fillColor ? true : false, true) || false; }
+    @computed get solidStk() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.color && (!i.rootDoc.strokeDash || i.rootDoc.strokeDash === "0") ? true : false, true) || false; }
+    @computed get dashdStk() { return !this.unStrokd && this.getField("strokeDash") || ""; }
+    @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; }
+    @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; }
+    @computed get widthStk() { return this.getField("strokeWidth") || "1"; }
+    @computed get markHead() { return this.getField("strokeStartMarker") || ""; }
+    @computed get markTail() { return this.getField("strokeEndMarker") || ""; }
+    @computed get shapeHgt() { return this.getField("_height"); }
+    @computed get shapeWid() { return this.getField("_width"); }
+    @computed get shapeXps() { return this.getField("x"); }
+    @computed get shapeYps() { return this.getField("y"); }
+    @computed get shapeRot() { return this.getField("rotation"); }
+    set unFilled(value) { this.colorFil = value ? "" : this._lastFill; }
+    set solidFil(value) { this.unFilled = !value; }
+    set colorFil(value) { value && (this._lastFill = value); this.selectedInk?.forEach(i => i.rootDoc.fillColor = value ? value : undefined); }
+    set colorStk(value) { value && (this._lastLine = value); this.selectedInk?.forEach(i => i.rootDoc.color = value ? value : undefined); }
+    set markHead(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeStartMarker = value); }
+    set markTail(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeEndMarker = value); }
+    set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; }
+    set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; }
+    set dashdStk(value) {
+        value && (this._lastDash = value) && (this.unStrokd = false);
+        this.selectedInk?.forEach(i => i.rootDoc.strokeDash = value ? this._lastDash : undefined);
     }
-    set _currXpos(value) { this.inks?.forEach(i => i.rootDoc.x = Number(value)); }
-    set _currYpos(value) { this.inks?.forEach(i => i.rootDoc.y = Number(value)); }
-    set _currRotation(value) { this.inks?.forEach(i => i.rootDoc.rotation = Number(value)); }
-    set _currStrokeWidth(value) { this.inks?.forEach(i => i.rootDoc.strokeWidth = Number(value)); }
-    set _currSizeWidth(value) {
-        this.inks?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
+    set shapeXps(value) { this.selectedInk?.forEach(i => i.rootDoc.x = Number(value)); }
+    set shapeYps(value) { this.selectedInk?.forEach(i => i.rootDoc.y = Number(value)); }
+    set shapeRot(value) { this.selectedInk?.forEach(i => i.rootDoc.rotation = Number(value)); }
+    set widthStk(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeWidth = Number(value)); }
+    set shapeWid(value) {
+        this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
             const oldWidth = NumCast(i.rootDoc._width);
             i.rootDoc._width = Number(value);
             this._lock && (i.rootDoc._height = (i.rootDoc._width * NumCast(i.rootDoc._height)) / oldWidth);
         });
     }
-    set _currSizeHeight(value) {
-        this.inks?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
+    set shapeHgt(value) {
+        this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
             const oldHeight = NumCast(i.rootDoc._height);
             i.rootDoc._height = Number(value);
             this._lock && (i.rootDoc._width = (i.rootDoc._height * NumCast(i.rootDoc._width)) / oldHeight);
@@ -90,72 +87,41 @@ export default class FormatShapePane extends AntimodeMenu {
         super(props);
         FormatShapePane.Instance = this;
         this._canFade = false;
-        this.Pinned = BoolCast(Doc.UserDoc()["formatShapePane-pinned"]);
+        this.Pinned = BoolCast(Doc.UserDoc()["menuFormatShape-pinned"]);
     }
 
     @action
     closePane = () => {
-        this.jumpTo(-300, -300);
+        this.fadeOut(false);
         this.Pinned = false;
     }
 
     @action
     upDownButtons = (dirs: string, field: string) => {
         switch (field) {
-            case "horizontal": this.inks?.forEach(i => i.rootDoc.x = NumCast(i.rootDoc.x) + (dirs === "up" ? 10 : -10)); break;
-            case "vertical": this.inks?.forEach(i => i.rootDoc.y = NumCast(i.rootDoc.y) + (dirs === "up" ? 10 : -10)); break;
-            case "rotation": this.rotate((dirs === "up" ? .1 : -.1)); break;
-            case "width": this.inks?.forEach(i => i.rootDoc.strokeWidth = NumCast(i.rootDoc.strokeWidth) + (dirs === "up" ? .1 : -.1)); break;
-            case "sizeWidth":
-                this.inks?.forEach(i => {
-                    const doc = i.rootDoc;
-                    if (doc._width && doc._height) {
-                        const oldWidth = NumCast(doc._width);
-                        const oldHeight = NumCast(doc._height);
-                        doc._width = NumCast(doc._width) + (dirs === "up" ? 10 : - 10);
-                        if (this._lock) {
-                            doc._height = (NumCast(doc._width) * oldHeight) / oldWidth;
-                        }
-                    }
-                });
+            case "rot": this.rotate((dirs === "up" ? .1 : -.1)); break;
+            case "Xps": this.selectedInk?.forEach(i => i.rootDoc.x = NumCast(i.rootDoc.x) + (dirs === "up" ? 10 : -10)); break;
+            case "Yps": this.selectedInk?.forEach(i => i.rootDoc.y = NumCast(i.rootDoc.y) + (dirs === "up" ? 10 : -10)); break;
+            case "stk": this.selectedInk?.forEach(i => i.rootDoc.strokeWidth = NumCast(i.rootDoc.strokeWidth) + (dirs === "up" ? .1 : -.1)); break;
+            case "wid": this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
+                const oldWidth = NumCast(i.rootDoc._width);
+                i.rootDoc._width = oldWidth + (dirs === "up" ? 10 : - 10);
+                this._lock && (i.rootDoc._height = (i.rootDoc._width / oldWidth * NumCast(i.rootDoc._height)));
+            });
                 break;
-            case "sizeHeight":
-                this.inks?.forEach(i => {
-                    const doc = i.rootDoc;
-                    if (doc._width && doc._height) {
-                        const oldWidth = NumCast(doc._width);
-                        const oldHeight = NumCast(doc._height);
-                        doc._height = NumCast(doc._height) + (dirs === "up" ? 10 : - 10);
-                        if (this._lock) {
-                            doc._width = (NumCast(doc._height) * oldWidth) / oldHeight;
-                        }
-                    }
-                });
+            case "hgt": this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
+                const oldHeight = NumCast(i.rootDoc._height);
+                i.rootDoc._height = oldHeight + (dirs === "up" ? 10 : - 10);
+                this._lock && (i.rootDoc._width = (i.rootDoc._height / oldHeight * NumCast(i.rootDoc._width)));
+            });
                 break;
         }
     }
 
-    @computed get close() {
-        return <button className="antimodeMenu-button" key="close" onPointerDown={action(() => this.closePane())} style={{ position: "absolute", right: 0 }}>
-            X
-        </button>;
-    }
-
-    //select either coor&fill or size&position
-    @computed get modes() {
-        return <div className="antimodeMenu-button-tab" key="modes">
-            {this._mode.map(mode =>
-                <button className="antimodeMenu-button" key={mode} onPointerDown={action(() => { this._currMode = mode; })}
-                    style={{ backgroundColor: this._currMode === mode ? "121212" : "", position: "relative", top: 30 }}>
-                    <FontAwesomeIcon icon={mode as IconProp} size="lg" />
-                </button>)}
-        </div>;
-    }
-
     @action
     rotate = (degrees: number) => {
-        SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
-            const doc = Document(element.rootDoc);
+        this.selectedInk?.forEach(action(inkView => {
+            const doc = Document(inkView.rootDoc);
             if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
                 const angle = Number(degrees) - Number(doc.rotation);
                 doc.rotation = Number(degrees);
@@ -183,117 +149,13 @@ export default class FormatShapePane extends AntimodeMenu {
                     const top2 = Math.min(...ys2);
                     const right2 = Math.max(...xs2);
                     const bottom2 = Math.max(...ys2);
-                    doc._height = (bottom2 - top2) * element.props.ScreenToLocalTransform().Scale;
-                    doc._width = (right2 - left2) * element.props.ScreenToLocalTransform().Scale;
+                    doc._height = (bottom2 - top2) * inkView.props.ScreenToLocalTransform().Scale;
+                    doc._width = (right2 - left2) * inkView.props.ScreenToLocalTransform().Scale;
                 }
             }
         }));
     }
 
-    @computed get subMenu() {
-        const fillCheck = <div key="fill" style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
-            <input id="nofill" style={{ width: "inherit", position: "absolute" }} type="radio" checked={this._noFill} onChange={action(() => this._noFill = true)} />
-            No Fill
-            <br />
-            <input id="solidfill" style={{ width: "inherit", position: "absolute" }} type="radio" checked={this._solidFill} onChange={action(() => this._solidFill = true)} />
-            Solid Fill
-            <br />
-            <br />
-            {this._solidFill ? "Color" : ""}
-            {this._solidFill ? this.fillButton : ""}
-            {this._fillBtn && this._solidFill ? this.fillPicker : ""}
-        </div>;
-
-        const arrows = <>
-            <input id="arrowStart" key="arrowstart" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._arrowStart !== ""} onChange={action(() => this._arrowStart = this._arrowStart ? "" : "arrow")} />
-            Arrow Head
-            <br />
-            <input id="arrowEnd" key="arrowend" style={{ width: "inherit", position: "absolute" }} type="checkbox" checked={this._arrowEnd !== ""} onChange={action(() => this._arrowEnd = this._arrowEnd ? "" : "arrow")} />
-            Arrow End
-            <br />
-        </>;
-
-        const lineCheck = <div key="lineCheck" style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
-            <input id="noLine" style={{ width: "inherit", position: "absolute" }} type="radio" checked={this._noLine} onChange={action(() => this._noLine = true)} />
-                No Line
-            <br />
-            <input id="solidLine" style={{ width: "inherit", position: "absolute" }} type="radio" checked={this._solidLine} onChange={action(() => this._solidLine = true)} />
-                Solid Line
-            <br />
-            <input id="dashLine" style={{ width: "inherit", position: "absolute" }} type="radio" checked={this._dashLine ? true : false} onChange={action(() => this._dashLine = "2")} />
-                Dash Line
-            <br />
-            <br />
-            {(this._solidLine || this._dashLine) ? "Color" : ""}
-            {(this._solidLine || this._dashLine) ? this.lineButton : ""}
-            {this._lineBtn && (this._solidLine || this._dashLine) ? this.linePicker : ""}
-            <br />
-            {(this._solidLine || this._dashLine) ? "Width" : ""}
-            {(this._solidLine || this._dashLine) ? this.widthInput : ""}
-            {(this._solidLine || this._dashLine) ? <input type="range" defaultValue={Number(this._currStrokeWidth || "1")} min={1} max={100} onChange={e => this._currStrokeWidth = e.target.value} /> : (null)}
-            <br />
-            <br />
-            {(this._solidLine || this._dashLine) ? arrows : ""}
-        </div>;
-
-        const sizeCheck = <div key="sizeCheck" style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
-            Height {this.sizeHeightInput}
-            <br />
-            <br />
-
-            Width {this.sizeWidthInput}
-            <br />
-            <br />
-
-            <input id="lock" style={{ width: "inherit", position: "absolute", right: 0 }} type="checkbox" checked={this._lock} onChange={action(() => this._lock = !this._lock)} />
-                Lock Ratio
-            <br />
-            <br />
-
-            Rotation {this.rotationInput}
-            <br />
-            <br />
-        </div>;
-
-        const positionCheck = <div key="posCheck" style={{ width: "inherit", backgroundColor: "#323232", color: "white", }}>
-            Horizontal {this.positionHorizontalInput}
-            <br />
-            <br />
-
-            Vertical {this.positionVerticalInput}
-            <br />
-            <br />
-        </div>;
-
-        return <div className="antimodeMenu-sub" key="submenu" style={{ position: "absolute", width: "inherit", top: 60 }}>
-            {this._subMenu.map((subMenu, i) => {
-                if (subMenu === "fill" || subMenu === "line") {
-                    return <div key={subMenu} style={{ width: "inherit" }}>
-                        <button className="antimodeMenu-button"
-                            onPointerDown={action(() => { this._subOpen[i] = this._subOpen[i] ? false : true; })}
-                            style={{ backgroundColor: "121212", position: "relative", display: this._currMode === "fill-drip" ? "" : "none", width: "inherit" }}>
-                            {this._subOpen[i] ? "▼" : "▶︎"}
-                            {subMenu}
-                        </button>
-                        {this._currMode === "fill-drip" && subMenu === "fill" && this._subOpen[0] ? fillCheck : ""}
-                        {this._currMode === "fill-drip" && subMenu === "line" && this._subOpen[1] ? lineCheck : ""}
-                    </div>;
-                }
-                else if (subMenu === "size" || subMenu === "position") {
-                    return <div key={subMenu} style={{ width: "inherit" }}>
-                        <button className="antimodeMenu-button"
-                            onPointerDown={action(() => { this._subOpen[i] = this._subOpen[i] ? false : true; })}
-                            style={{ backgroundColor: "121212", position: "relative", display: this._currMode === "ruler-combined" ? "" : "none", width: "inherit" }}>
-                            {this._subOpen[i] ? "▼" : "▶︎"}
-                            {subMenu}
-                        </button>
-                        {this._currMode === "ruler-combined" && subMenu === "size" && this._subOpen[2] ? sizeCheck : ""}
-                        {this._currMode === "ruler-combined" && subMenu === "position" && this._subOpen[3] ? positionCheck : ""}
-                    </div>;
-                }
-            })}
-        </div>;
-    }
 
     colorPicker(setter: (color: string) => {}) {
         return <div className="btn-group-palette" key="colorpicker" >
@@ -325,37 +187,119 @@ export default class FormatShapePane extends AntimodeMenu {
                 <FontAwesomeIcon icon="fill-drip" size="lg" />
                 <div className="color-previewI" style={{ backgroundColor: value ?? "121212" }} />
             </button>
-            <br></br>
-            <br></br>
+            <br /> <br />
         </>;
     }
 
-    @computed get fillButton() { return this.colorButton(this._currFill, () => this._fillBtn = !this._fillBtn); }
-    @computed get lineButton() { return this.colorButton(this._currColor, () => this._lineBtn = !this._lineBtn); }
+    @computed get fillButton() { return this.colorButton(this.colorFil, () => this._fillBtn = !this._fillBtn); }
+    @computed get lineButton() { return this.colorButton(this.colorStk, () => this._lineBtn = !this._lineBtn); }
 
-    @computed get fillPicker() { return this.colorPicker((color: string) => this._currFill = color); }
-    @computed get linePicker() { return this.colorPicker((color: string) => this._currColor = color); }
+    @computed get fillPicker() { return this.colorPicker((color: string) => this.colorFil = color); }
+    @computed get linePicker() { return this.colorPicker((color: string) => this.colorStk = color); }
 
-    @computed get widthInput() { return this.inputBox("width", this._currStrokeWidth, (val: string) => this._currStrokeWidth = val); }
-    @computed get sizeHeightInput() { return this.inputBox("height", this._currSizeHeight, (val: string) => this._currSizeHeight = val); }
-    @computed get sizeWidthInput() { return this.inputBox("height", this._currSizeWidth, (val: string) => this._currSizeWidth = val); }
-    @computed get rotationInput() { return this.inputBox("rotation", this._currRotation, (val: string) => this._currRotation = val); }
-    @computed get positionHorizontalInput() { return this.inputBox("horizontal", this._currXpos, (val: string) => this._currXpos = val); }
-    @computed get positionVerticalInput() { return this.inputBox("vertical", this._currYpos, (val: string) => this._currYpos = val); }
+    @computed get stkInput() { return this.inputBox("stk", this.widthStk, (val: string) => this.widthStk = val); }
+    @computed get hgtInput() { return this.inputBox("hgt", this.shapeHgt, (val: string) => this.shapeHgt = val); }
+    @computed get widInput() { return this.inputBox("wid", this.shapeWid, (val: string) => this.shapeWid = val); }
+    @computed get rotInput() { return this.inputBox("rot", this.shapeRot, (val: string) => this.shapeRot = val); }
+    @computed get XpsInput() { return this.inputBox("Xps", this.shapeXps, (val: string) => this.shapeXps = val); }
+    @computed get YpsInput() { return this.inputBox("Yps", this.shapeYps, (val: string) => this.shapeYps = val); }
 
-    render() {
-        return this.getElementVert([this.close, this.modes, this.subMenu]);
+    @computed get propertyGroupItems() {
+        const fillCheck = <div key="fill" style={{ display: this._subOpen[0] ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}>
+            <input className="formatShapePane-inputBtn" type="radio" checked={this.unFilled} onChange={action(() => this.unFilled = true)} />
+                No Fill
+            <br />
+            <input className="formatShapePane-inputBtn" type="radio" checked={this.solidFil} onChange={action(() => this.solidFil = true)} />
+                Solid Fill
+            <br /> <br />
+            {this.solidFil ? "Color" : ""}
+            {this.solidFil ? this.fillButton : ""}
+            {this._fillBtn && this.solidFil ? this.fillPicker : ""}
+        </div>;
+
+        const markers = <>
+            <input key="markHead" className="formatShapePane-inputBtn" type="checkbox" checked={this.markHead !== ""} onChange={action(() => this.markHead = this.markHead ? "" : "arrow")} />
+                Arrow Head
+            <br />
+            <input key="markTail" className="formatShapePane-inputBtn" type="checkbox" checked={this.markTail !== ""} onChange={action(() => this.markTail = this.markTail ? "" : "arrow")} />
+                Arrow End
+            <br />
+        </>;
+
+        const lineCheck = <div key="lineCheck" style={{ display: this._subOpen[1] ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}>
+            <input className="formatShapePane-inputBtn" type="radio" checked={this.unStrokd} onChange={action(() => this.unStrokd = true)} />
+                No Line
+            <br />
+            <input className="formatShapePane-inputBtn" type="radio" checked={this.solidStk} onChange={action(() => this.solidStk = true)} />
+                Solid Line
+            <br />
+            <input className="formatShapePane-inputBtn" type="radio" checked={this.dashdStk ? true : false} onChange={action(() => this.dashdStk = "2")} />
+                Dash Line
+            <br />
+            <br />
+            {(this.solidStk || this.dashdStk) ? "Color" : ""}
+            {(this.solidStk || this.dashdStk) ? this.lineButton : ""}
+            {(this.solidStk || this.dashdStk) && this._lineBtn ? this.linePicker : ""}
+            <br />
+            {(this.solidStk || this.dashdStk) ? "Width" : ""}
+            {(this.solidStk || this.dashdStk) ? this.stkInput : ""}
+            {(this.solidStk || this.dashdStk) ? <input type="range" defaultValue={Number(this.widthStk)} min={1} max={100} onChange={e => this.widthStk = e.target.value} /> : (null)}
+            <br /> <br />
+            {(this.solidStk || this.dashdStk) ? markers : ""}
+        </div>;
+
+        const sizeCheck = <div key="sizeCheck" style={{ display: this._subOpen[2] ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}>
+            Height {this.hgtInput}
+            <br /> <br />
+                Width {this.widInput}
+            <br /> <br />
+            <input className="formatShapePane-inputBtn" style={{ right: 0 }} type="checkbox" checked={this._lock} onChange={action(() => this._lock = !this._lock)} />
+                Lock Ratio
+            <br />  <br />
+                Rotation {this.rotInput}
+            <br /> <br />
+        </div>;
+
+        const positionCheck = <div key="posCheck" style={{ display: this._subOpen[3] ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}>
+            Horizontal {this.XpsInput}
+            <br /> <br />
+            Vertical {this.YpsInput}
+            <br /> <br />
+        </div>;
+
+        const subMenus = this._currMode === "fill-drip" ? [`fill`, `line`] : [`size`, `position`];
+        const menuItems = this._currMode === "fill-drip" ? [fillCheck, lineCheck] : [sizeCheck, positionCheck];
+        const indexOffset = this._currMode === "fill-drip" ? 0 : 2;
+        return <div className="antimodeMenu-sub" key="submenu" style={{ position: "absolute", width: "inherit", top: 60 }}>
+            {subMenus.map((subMenu, i) =>
+                <div key={subMenu} style={{ width: "inherit" }}>
+                    <button className="antimodeMenu-button" onPointerDown={action(() => this._subOpen[i + indexOffset] = !this._subOpen[i + indexOffset])}
+                        style={{ backgroundColor: "121212", position: "relative", width: "inherit" }}>
+                        {this._subOpen[i + indexOffset] ? "▼" : "▶︎"}
+                        {subMenu}
+                    </button>
+                    {menuItems[i]}
+                </div>)}
+        </div>;
+    }
+
+    @computed get closeBtn() {
+        return <button className="antimodeMenu-button" key="close" onPointerDown={action(() => this.closePane())} style={{ position: "absolute", right: 0 }}>
+            X
+        </button>;
+    }
+
+    @computed get propertyGroupBtn() {
+        return <div className="antimodeMenu-button-tab" key="modes">
+            {this._mode.map(mode =>
+                <button className="antimodeMenu-button" key={mode} onPointerDown={action(() => this._currMode = mode)}
+                    style={{ backgroundColor: this._currMode === mode ? "121212" : "", position: "relative", top: 30 }}>
+                    <FontAwesomeIcon icon={mode as IconProp} size="lg" />
+                </button>)}
+        </div>;
     }
-}
-Scripting.addGlobal(function activatePen2(penBtn: any) {
-    if (penBtn) {
-        //no longer changes to inkmode
-        // Doc.SetSelectedTool(InkTool.Pen);
-        FormatShapePane.Instance.jumpTo(300, 300);
-        FormatShapePane.Instance.Pinned = true;
-    } else {
-        // Doc.SetSelectedTool(InkTool.None);
-        FormatShapePane.Instance.Pinned = false;
-        FormatShapePane.Instance.fadeOut(true);
+
+    render() {
+        return this.getElementVert([this.closeBtn, this.propertyGroupBtn, this.propertyGroupItems]);
     }
-});
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
index 7f0bb5364..15707ad9e 100644
--- a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
@@ -62,13 +62,13 @@ export default class InkOptionsMenu extends AntimodeMenu {
         super(props);
         InkOptionsMenu.Instance = this;
         this._canFade = false; // don't let the inking menu fade away
-        this.Pinned = BoolCast(Doc.UserDoc()["inkOptionsMenu-pinned"]);
+        this.Pinned = BoolCast(Doc.UserDoc()["menuInkOptions-pinned"]);
 
     }
 
     @action
     toggleMenuPin = (e: React.MouseEvent) => {
-        Doc.UserDoc()["inkOptionsMenu-pinned"] = this.Pinned = !this.Pinned;
+        Doc.UserDoc()["menuInkOptions-pinned"] = this.Pinned = !this.Pinned;
         if (!this.Pinned) {
             // this.fadeOut(true);
         }
@@ -426,15 +426,10 @@ export default class InkOptionsMenu extends AntimodeMenu {
 }
 Scripting.addGlobal(function activatePen(penBtn: any) {
     if (penBtn) {
-        //no longer changes to inkmode
-        // Doc.SetSelectedTool(InkTool.Pen);
         InkOptionsMenu.Instance.jumpTo(300, 300);
         InkOptionsMenu.Instance.Pinned = true;
-
     } else {
-        // Doc.SetSelectedTool(InkTool.None);
         InkOptionsMenu.Instance.Pinned = false;
         InkOptionsMenu.Instance.fadeOut(true);
-
     }
 });
-- 
cgit v1.2.3-70-g09d2


From 0e2a5196b47c05eab74d0aae06c2db4a9528f0fb Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 13 Jul 2020 20:44:03 -0400
Subject: switched context menus to be less hierarchical.

---
 src/client/util/CurrentUserUtils.ts                |  2 -
 src/client/util/SettingsManager.scss               |  2 +-
 src/client/util/SettingsManager.tsx                | 14 ++++++-
 src/client/views/ContextMenuItem.tsx               |  6 +++
 .../views/collections/CollectionCarousel3DView.tsx | 13 +-----
 .../views/collections/CollectionCarouselView.tsx   | 14 +------
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/collections/CollectionView.tsx    |  6 +--
 .../collectionFreeForm/FormatShapePane.tsx         | 26 ++++++------
 .../collectionGrid/CollectionGridView.tsx          |  5 +--
 src/client/views/nodes/DocumentView.tsx            | 38 ++++++++---------
 src/client/views/nodes/ImageBox.tsx                | 48 +++++++++++-----------
 src/client/views/nodes/LabelBox.tsx                |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 48 +++++++++++-----------
 14 files changed, 111 insertions(+), 115 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/FormatShapePane.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 39781a5ce..048ab9edc 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -423,8 +423,6 @@ export class CurrentUserUtils {
             // { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activeInkPen = this;', ischecked: `sameDocs(this.activeInkPen, this)`, backgroundColor: "white", activeInkPen: doc },
             { title: "Drag a document previewer", label: "Prev", icon: "expand", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory,true)', dragFactory: doc.emptyDocHolder as Doc },
             { title: "Toggle a Calculator REPL", label: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' },
-            { title: "Connect a Google Account", label: "Google Account", icon: "external-link-alt", click: 'GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true)' },
-            { title: "Connect a Hypothesis Account", label: "Hypothesis Account", icon: "heading", click: 'HypothesisAuthenticationManager.Instance.fetchOrGenerateAccessToken(true)' },
         ];
 
     }
diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss
index 13c65042c..6d394a38d 100644
--- a/src/client/util/SettingsManager.scss
+++ b/src/client/util/SettingsManager.scss
@@ -56,7 +56,7 @@
         .settings-type {
             display: flex;
             flex-direction: column;
-            flex-basis: 30%;
+            flex-basis: 45%;
 
         }
 
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 9888cce48..f7ca3942b 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -11,6 +11,8 @@ import { Networking } from "../Network";
 import { CurrentUserUtils } from "./CurrentUserUtils";
 import { Utils } from "../../Utils";
 import { Doc } from "../../fields/Doc";
+import HypothesisAuthenticationManager from "../apis/HypothesisAuthenticationManager";
+import GoogleAuthenticationManager from "../apis/GoogleAuthenticationManager";
 
 library.add(fa.faWindowClose);
 
@@ -83,6 +85,14 @@ export default class SettingsManager extends React.Component<{}> {
     noviceToggle = (event: any) => {
         Doc.UserDoc().noviceMode = !Doc.UserDoc().noviceMode;
     }
+    @action
+    googleAuthorize = (event: any) => {
+        GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true)
+    }
+    @action
+    hypothesisAuthorize = (event: any) => {
+        HypothesisAuthenticationManager.Instance.fetchAccessToken(true)
+    }
 
     private get settingsInterface() {
         return (
@@ -96,7 +106,9 @@ export default class SettingsManager extends React.Component<{}> {
                 <div className="settings-body">
                     <div className="settings-type">
                         <button onClick={this.onClick} value="password">reset password</button>
-                        <button onClick={this.noviceToggle} value="data">{`toggle ${Doc.UserDoc().noviceMode ? "developer" : "novice"} mode`}</button>
+                        <button onClick={this.noviceToggle} value="data">{`Set ${Doc.UserDoc().noviceMode ? "developer" : "novice"} mode`}</button>
+                        <button onClick={this.googleAuthorize} value="data">{`Link to Google`}</button>
+                        <button onClick={this.hypothesisAuthorize} value="data">{`Link to Hypothes.is`}</button>
                         <button onClick={() => window.location.assign(Utils.prepend("/logout"))}>
                             {CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}
                         </button>
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index 99840047f..68ebd8e14 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -19,6 +19,7 @@ export interface OriginalMenuProps {
 export interface SubmenuProps {
     description: string;
     subitems: ContextMenuProps[];
+    noexpand?: boolean;
     icon: IconProp; //maybe should be optional (icon?)
     closeMenu?: () => void;
 }
@@ -96,6 +97,11 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
                 <div className="contextMenu-subMenu-cont" style={{ marginLeft: "25%", left: "0px", marginTop }}>
                     {this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)}
                 </div>;
+            if (!("noexpand" in this.props)) {
+                return <>
+                    {this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)}
+                </>;
+            }
             return (
                 <div className={"contextMenu-item" + (this.props.selected ? " contextMenu-itemSelected" : "")} style={{ alignItems: where }}
                     onMouseLeave={this.onPointerLeave} onMouseEnter={this.onPointerEnter}>
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 1344b70f4..8f1cd5311 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -108,17 +108,6 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume
         }, 1500);
     }
 
-    onContextMenu = (e: React.MouseEvent): void => {
-        // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
-        if (!e.isPropagationStopped()) {
-            ContextMenu.Instance.addItem({
-                description: "Make Hero Image", event: () => {
-                    const index = NumCast(this.layoutDoc._itemIndex);
-                    (this.dataDoc || Doc.GetProto(this.props.Document)).hero = ObjectField.MakeCopy(this.childLayoutPairs[index].layout.data as ObjectField);
-                }, icon: "plus"
-            });
-        }
-    }
     _downX = 0;
     _downY = 0;
     onPointerDown = (e: React.PointerEvent) => {
@@ -184,7 +173,7 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume
         const index = NumCast(this.layoutDoc._itemIndex);
         const translateX = (1 - index) / this.childLayoutPairs.length * 100;
 
-        return <div className="collectionCarousel3DView-outer" onClick={this.onClick} onPointerDown={this.onPointerDown} ref={this.createDashEventsTarget} onContextMenu={this.onContextMenu}>
+        return <div className="collectionCarousel3DView-outer" onClick={this.onClick} onPointerDown={this.onPointerDown} ref={this.createDashEventsTarget}>
             <div className="carousel-wrapper" style={{ transform: `translateX(calc(${translateX}%` }}>
                 {this.content}
             </div>
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index bd0e4fc9a..b3fecfb91 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -83,18 +83,6 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
         </>;
     }
 
-
-    onContextMenu = (e: React.MouseEvent): void => {
-        // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
-        if (!e.isPropagationStopped()) {
-            ContextMenu.Instance?.addItem({
-                description: "Make Hero Image", event: () => {
-                    const index = NumCast(this.layoutDoc._itemIndex);
-                    (this.dataDoc || Doc.GetProto(this.props.Document)).hero = ObjectField.MakeCopy(this.childLayoutPairs[index].layout.data as ObjectField);
-                }, icon: "plus"
-            });
-        }
-    }
     _downX = 0;
     _downY = 0;
     onPointerDown = (e: React.PointerEvent) => {
@@ -119,7 +107,7 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
     }
 
     render() {
-        return <div className="collectionCarouselView-outer" onClick={this.onClick} onPointerDown={this.onPointerDown} ref={this.createDashEventsTarget} onContextMenu={this.onContextMenu}>
+        return <div className="collectionCarouselView-outer" onClick={this.onClick} onPointerDown={this.onPointerDown} ref={this.createDashEventsTarget}>
             {this.content}
             {this.props.Document._chromeStatus !== "replaced" ? this.buttons : (null)}
         </div>;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 26c41f524..dbd39d8df 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -780,7 +780,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         onClicks.push({
             description: "Edit onChecked Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.doc, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit"
         });
-        !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
+        !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "hand-point-right" });
     }
     outerXf = () => Utils.GetScreenTransform(this._mainEle!);
     onTreeDrop = (e: React.DragEvent) => this.onExternalDrop(e, {});
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index df21d6a28..cbd1ac9af 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -272,7 +272,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
         }
         addExtras && subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
-        !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: subItems, icon: "eye" });
+        !existingVm && ContextMenu.Instance.addItem({ description: category, noexpand: true, subitems: subItems, icon: "eye" });
     }
 
     onContextMenu = (e: React.MouseEvent): void => {
@@ -294,7 +294,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             if (this.props.Document.childClickedOpenTemplateView instanceof Doc) {
                 layoutItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childClickedOpenTemplateView as Doc, "onRight"), icon: "project-diagram" });
             }
-            layoutItems.push({ description: `${this.props.Document.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.props.Document.isInPlaceContainer = !this.props.Document.isInPlaceContainer, icon: "project-diagram" });
+            !Doc.UserDoc().noviceMode && layoutItems.push({ description: `${this.props.Document.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.props.Document.isInPlaceContainer = !this.props.Document.isInPlaceContainer, icon: "project-diagram" });
 
             !existing && cm.addItem({ description: "Options...", subitems: layoutItems, icon: "hand-point-right" });
 
@@ -314,7 +314,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                     icon: "edit",
                     event: () => this.props.Document[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)),
                 }));
-            !existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
+            !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "hand-point-right" });
 
             if (!Doc.UserDoc().noviceMode) {
                 const more = cm.findByDescription("More...");
diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
index 56ba9e96a..9d9ce7f36 100644
--- a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
+++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
@@ -11,6 +11,7 @@ import { DocumentType } from "../../../documents/DocumentTypes";
 import { SelectionManager } from "../../../util/SelectionManager";
 import AntimodeMenu from "../../AntimodeMenu";
 import "./FormatShapePane.scss";
+import { undoBatch } from "../../../util/UndoManager";
 
 @observer
 export default class FormatShapePane extends AntimodeMenu {
@@ -118,6 +119,7 @@ export default class FormatShapePane extends AntimodeMenu {
         }
     }
 
+    @undoBatch
     @action
     rotate = (degrees: number) => {
         this.selectedInk?.forEach(action(inkView => {
@@ -160,7 +162,7 @@ export default class FormatShapePane extends AntimodeMenu {
     colorPicker(setter: (color: string) => {}) {
         return <div className="btn-group-palette" key="colorpicker" >
             {this._palette.map(color =>
-                <button className="antimodeMenu-button" key={color} onPointerDown={action(() => setter(color))} style={{ zIndex: 1001, position: "relative" }}>
+                <button className="antimodeMenu-button" key={color} onPointerDown={undoBatch(action(() => setter(color)))} style={{ zIndex: 1001, position: "relative" }}>
                     <div className="color-previewII" style={{ backgroundColor: color }} />
                 </button>)}
         </div>;
@@ -171,11 +173,11 @@ export default class FormatShapePane extends AntimodeMenu {
                 type="text" value={value}
                 onChange={e => setter(e.target.value)}
                 autoFocus />
-            <button className="antiMenu-Buttonup" key="up" onPointerDown={action(() => this.upDownButtons("up", key))}>
+            <button className="antiMenu-Buttonup" key="up" onPointerDown={undoBatch(action(() => this.upDownButtons("up", key)))}>
                 ˄
             </button>
             <br />
-            <button className="antiMenu-Buttonup" key="down" onPointerDown={action(() => this.upDownButtons("down", key))} style={{ marginTop: -8 }}>
+            <button className="antiMenu-Buttonup" key="down" onPointerDown={undoBatch(action(() => this.upDownButtons("down", key)))} style={{ marginTop: -8 }}>
                 ˅
             </button>
         </>;
@@ -183,7 +185,7 @@ export default class FormatShapePane extends AntimodeMenu {
 
     colorButton(value: string, setter: () => {}) {
         return <>
-            <button className="antimodeMenu-button" key="fill" onPointerDown={action(e => setter())} style={{ position: "absolute", right: 80 }}>
+            <button className="antimodeMenu-button" key="fill" onPointerDown={undoBatch(action(e => setter()))} style={{ position: "absolute", right: 80 }}>
                 <FontAwesomeIcon icon="fill-drip" size="lg" />
                 <div className="color-previewI" style={{ backgroundColor: value ?? "121212" }} />
             </button>
@@ -206,10 +208,10 @@ export default class FormatShapePane extends AntimodeMenu {
 
     @computed get propertyGroupItems() {
         const fillCheck = <div key="fill" style={{ display: this._subOpen[0] ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}>
-            <input className="formatShapePane-inputBtn" type="radio" checked={this.unFilled} onChange={action(() => this.unFilled = true)} />
+            <input className="formatShapePane-inputBtn" type="radio" checked={this.unFilled} onChange={undoBatch(action(() => this.unFilled = true))} />
                 No Fill
             <br />
-            <input className="formatShapePane-inputBtn" type="radio" checked={this.solidFil} onChange={action(() => this.solidFil = true)} />
+            <input className="formatShapePane-inputBtn" type="radio" checked={this.solidFil} onChange={undoBatch(action(() => this.solidFil = true))} />
                 Solid Fill
             <br /> <br />
             {this.solidFil ? "Color" : ""}
@@ -218,22 +220,22 @@ export default class FormatShapePane extends AntimodeMenu {
         </div>;
 
         const markers = <>
-            <input key="markHead" className="formatShapePane-inputBtn" type="checkbox" checked={this.markHead !== ""} onChange={action(() => this.markHead = this.markHead ? "" : "arrow")} />
+            <input key="markHead" className="formatShapePane-inputBtn" type="checkbox" checked={this.markHead !== ""} onChange={undoBatch(action(() => this.markHead = this.markHead ? "" : "arrow"))} />
                 Arrow Head
             <br />
-            <input key="markTail" className="formatShapePane-inputBtn" type="checkbox" checked={this.markTail !== ""} onChange={action(() => this.markTail = this.markTail ? "" : "arrow")} />
+            <input key="markTail" className="formatShapePane-inputBtn" type="checkbox" checked={this.markTail !== ""} onChange={undoBatch(action(() => this.markTail = this.markTail ? "" : "arrow"))} />
                 Arrow End
             <br />
         </>;
 
         const lineCheck = <div key="lineCheck" style={{ display: this._subOpen[1] ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}>
-            <input className="formatShapePane-inputBtn" type="radio" checked={this.unStrokd} onChange={action(() => this.unStrokd = true)} />
+            <input className="formatShapePane-inputBtn" type="radio" checked={this.unStrokd} onChange={undoBatch(action(() => this.unStrokd = true))} />
                 No Line
             <br />
-            <input className="formatShapePane-inputBtn" type="radio" checked={this.solidStk} onChange={action(() => this.solidStk = true)} />
+            <input className="formatShapePane-inputBtn" type="radio" checked={this.solidStk} onChange={undoBatch(action(() => this.solidStk = true))} />
                 Solid Line
             <br />
-            <input className="formatShapePane-inputBtn" type="radio" checked={this.dashdStk ? true : false} onChange={action(() => this.dashdStk = "2")} />
+            <input className="formatShapePane-inputBtn" type="radio" checked={this.dashdStk ? true : false} onChange={undoBatch(action(() => this.dashdStk = "2"))} />
                 Dash Line
             <br />
             <br />
@@ -253,7 +255,7 @@ export default class FormatShapePane extends AntimodeMenu {
             <br /> <br />
                 Width {this.widInput}
             <br /> <br />
-            <input className="formatShapePane-inputBtn" style={{ right: 0 }} type="checkbox" checked={this._lock} onChange={action(() => this._lock = !this._lock)} />
+            <input className="formatShapePane-inputBtn" style={{ right: 0 }} type="checkbox" checked={this._lock} onChange={undoBatch(action(() => this._lock = !this._lock))} />
                 Lock Ratio
             <br />  <br />
                 Rotation {this.rotInput}
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 2015ca930..188b88c41 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -1,7 +1,7 @@
 import { action, computed, Lambda, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from "react";
-import { Doc, Opt } from '../../../../fields/Doc';
+import { Doc, Opt, WidthSym } from '../../../../fields/Doc';
 import { documentSchema } from '../../../../fields/documentSchemas';
 import { Id } from '../../../../fields/FieldSymbols';
 import { makeInterface } from '../../../../fields/Schema';
@@ -248,8 +248,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
      */
     onContextMenu = () => {
         const displayOptionsMenu: ContextMenuProps[] = [];
-        displayOptionsMenu.push({ description: "Contents", event: () => this.props.Document.display = "contents", icon: "copy" });
-        displayOptionsMenu.push({ description: "Undefined", event: () => this.props.Document.display = undefined, icon: "exclamation" });
+        displayOptionsMenu.push({ description: "Toggle Content Display Style", event: () => this.props.Document.display = this.props.Document.display ? undefined : "contents", icon: "copy" });
         ContextMenu.Instance.addItem({ description: "Display", subitems: displayOptionsMenu, icon: "tv" });
     }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6ef976ceb..a586e81d8 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -770,6 +770,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const options = cm.findByDescription("Options...");
         const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
         const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
+        optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
         templateDoc && optionItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" });
         !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
 
@@ -783,14 +784,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link on Right", event: this.toggleFollowOnRight, icon: "concierge-bell" });
         onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: this.toggleLinkButtonBehavior, icon: "concierge-bell" });
         onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "edit" });
-        !existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
+        !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "hand-point-right" });
 
         const funcs: ContextMenuProps[] = [];
         if (this.layoutDoc.onDragStart) {
             funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
             funcs.push({ description: "Drag a Copy", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
             funcs.push({ description: "Drag Document", icon: "edit", event: () => this.layoutDoc.onDragStart = undefined });
-            cm.addItem({ description: "OnDrag...", subitems: funcs, icon: "asterisk" });
+            cm.addItem({ description: "OnDrag...", noexpand: true, subitems: funcs, icon: "asterisk" });
         }
 
         const more = cm.findByDescription("More...");
@@ -817,29 +818,28 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 // a.download = `DocExport-${this.props.Document[Id]}.zip`;
                 // a.click();
             });
+            moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
         }
-        moreItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
-        moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
         moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" });
         moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "external-link-alt" });
         !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
         cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!);
 
-        const help = cm.findByDescription("Help...");
-        const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
-        helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "onRight"), icon: "keyboard" });
-        helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
-        cm.addItem({ description: "Help...", subitems: helpItems, icon: "question" });
-
-        const existingAcls = cm.findByDescription("Privacy...");
-        const aclItems: ContextMenuProps[] = existingAcls && "subitems" in existingAcls ? existingAcls.subitems : [];
-        aclItems.push({ description: "Make Add Only", event: () => this.setAcl("addOnly"), icon: "concierge-bell" });
-        aclItems.push({ description: "Make Read Only", event: () => this.setAcl("readOnly"), icon: "concierge-bell" });
-        aclItems.push({ description: "Make Private", event: () => this.setAcl("ownerOnly"), icon: "concierge-bell" });
-        aclItems.push({ description: "Make Editable", event: () => this.setAcl("write"), icon: "concierge-bell" });
-        aclItems.push({ description: "Test Private", event: () => this.testAcl("ownerOnly"), icon: "concierge-bell" });
-        aclItems.push({ description: "Test Readonly", event: () => this.testAcl("readOnly"), icon: "concierge-bell" });
-        !existingAcls && cm.addItem({ description: "Privacy...", subitems: aclItems, icon: "question" });
+        // const help = cm.findByDescription("Help...");
+        // const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
+        // helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "onRight"), icon: "keyboard" });
+        // helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
+        // cm.addItem({ description: "Help...", subitems: helpItems, icon: "question" });
+
+        // const existingAcls = cm.findByDescription("Privacy...");
+        // const aclItems: ContextMenuProps[] = existingAcls && "subitems" in existingAcls ? existingAcls.subitems : [];
+        // aclItems.push({ description: "Make Add Only", event: () => this.setAcl("addOnly"), icon: "concierge-bell" });
+        // aclItems.push({ description: "Make Read Only", event: () => this.setAcl("readOnly"), icon: "concierge-bell" });
+        // aclItems.push({ description: "Make Private", event: () => this.setAcl("ownerOnly"), icon: "concierge-bell" });
+        // aclItems.push({ description: "Make Editable", event: () => this.setAcl("write"), icon: "concierge-bell" });
+        // aclItems.push({ description: "Test Private", event: () => this.testAcl("ownerOnly"), icon: "concierge-bell" });
+        // aclItems.push({ description: "Test Readonly", event: () => this.testAcl("readOnly"), icon: "concierge-bell" });
+        // !existingAcls && cm.addItem({ description: "Privacy...", subitems: aclItems, icon: "question" });
 
         // const recommender_subitems: ContextMenuProps[] = [];
 
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index d16aa528c..4eba21eab 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -157,29 +157,31 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
         const field = Cast(this.dataDoc[this.fieldKey], ImageField);
         if (field) {
             const funcs: ContextMenuProps[] = [];
-            funcs.push({ description: "Rotate", event: this.rotate, icon: "expand-arrows-alt" });
-            funcs.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" });
-            funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" });
-            // funcs.push({
-            //     description: "Reset Native Dimensions", event: action(async () => {
-            //         const curNW = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]);
-            //         const curNH = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]);
-            //         if (this.props.PanelWidth() / this.props.PanelHeight() > curNW / curNH) {
-            //             this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelHeight() * curNW / curNH;
-            //             this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelHeight();
-            //         } else {
-            //             this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelWidth();
-            //             this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelWidth() * curNH / curNW;
-            //         }
-            //     }), icon: "expand-arrows-alt"
-            // });
-
-            const existingAnalyze = ContextMenu.Instance?.findByDescription("Analyzers...");
-            const modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
-            modes.push({ description: "Generate Tags", event: this.generateMetadata, icon: "tag" });
-            modes.push({ description: "Find Faces", event: this.extractFaces, icon: "camera" });
-            //modes.push({ description: "Recommend", event: this.extractText, icon: "brain" });
-            !existingAnalyze && ContextMenu.Instance?.addItem({ description: "Analyzers...", subitems: modes, icon: "hand-point-right" });
+            funcs.push({ description: "Rotate Clockwise 90", event: this.rotate, icon: "expand-arrows-alt" });
+            if (!Doc.UserDoc().noviceMode) {
+                funcs.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" });
+                funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" });
+                // funcs.push({
+                //     description: "Reset Native Dimensions", event: action(async () => {
+                //         const curNW = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]);
+                //         const curNH = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]);
+                //         if (this.props.PanelWidth() / this.props.PanelHeight() > curNW / curNH) {
+                //             this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelHeight() * curNW / curNH;
+                //             this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelHeight();
+                //         } else {
+                //             this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelWidth();
+                //             this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelWidth() * curNH / curNW;
+                //         }
+                //     }), icon: "expand-arrows-alt"
+                // });
+
+                const existingAnalyze = ContextMenu.Instance?.findByDescription("Analyzers...");
+                const modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
+                modes.push({ description: "Generate Tags", event: this.generateMetadata, icon: "tag" });
+                modes.push({ description: "Find Faces", event: this.extractFaces, icon: "camera" });
+                //modes.push({ description: "Recommend", event: this.extractText, icon: "brain" });
+                !existingAnalyze && ContextMenu.Instance?.addItem({ description: "Analyzers...", subitems: modes, icon: "hand-point-right" });
+            }
 
             ContextMenu.Instance?.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
         }
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 836ef4149..0dfbdc5cf 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -41,7 +41,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps, LabelDocument
             }, icon: "trash"
         });
 
-        ContextMenu.Instance.addItem({ description: "OnClick...", subitems: funcs, icon: "asterisk" });
+        ContextMenu.Instance.addItem({ description: "OnClick...", noexpand: true, subitems: funcs, icon: "asterisk" });
     }
 
     @undoBatch
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 01ac765b9..f87b28c7f 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -479,7 +479,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             });
         });
         changeItems.push({ description: "FreeForm", event: () => DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" });
-        appearanceItems.push({ description: "Change Perspective...", subitems: changeItems, icon: "external-link-alt" });
+        appearanceItems.push({ description: "Change Perspective...", noexpand: true, subitems: changeItems, icon: "external-link-alt" });
         const uicontrols: ContextMenuProps[] = [];
         uicontrols.push({ description: "Toggle Sidebar", event: () => this.layoutDoc._showSidebar = !this.layoutDoc._showSidebar, icon: "expand-arrows-alt" });
         uicontrols.push({ description: "Toggle Dictation Icon", event: () => this.layoutDoc._showAudio = !this.layoutDoc._showAudio, icon: "expand-arrows-alt" });
@@ -489,10 +489,29 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 proto instanceof Doc && (proto.BROADCAST_MESSAGE = Cast(this.rootDoc[this.fieldKey], RichTextField)?.Text)), icon: "expand-arrows-alt"
         });
 
-        appearanceItems.push({ description: "UI Controls...", subitems: uicontrols, icon: "asterisk" });
+        appearanceItems.push({ description: "UI Controls...", noexpand: true, subitems: uicontrols, icon: "asterisk" });
         this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
         Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
-        appearanceItems.push({
+
+        !appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" });
+
+        const funcs: ContextMenuProps[] = [];
+
+        const highlighting: ContextMenuProps[] = [];
+        ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
+            highlighting.push({
+                description: (FormattedTextBox._highlights.indexOf(option) === -1 ? "Highlight " : "Unhighlight ") + option, event: () => {
+                    e.stopPropagation();
+                    if (FormattedTextBox._highlights.indexOf(option) === -1) {
+                        FormattedTextBox._highlights.push(option);
+                    } else {
+                        FormattedTextBox._highlights.splice(FormattedTextBox._highlights.indexOf(option), 1);
+                    }
+                    this.updateHighlights();
+                }, icon: "expand-arrows-alt"
+            }));
+        funcs.push({ description: "highlighting...", noexpand: true, subitems: highlighting, icon: "hand-point-right" });
+        funcs.push({
             description: "Convert to be a template style", event: () => {
                 if (!this.layoutDoc.isTemplateDoc) {
                     const title = StrCast(this.rootDoc.title);
@@ -517,29 +536,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 Doc.AddDocToList(Cast(Doc.UserDoc()["template-notes"], Doc, null), "data", this.rootDoc);
             }, icon: "eye"
         });
-        !appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" });
+        funcs.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
 
-        const funcs: ContextMenuProps[] = [];
-
-        //funcs.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
-        funcs.push({ description: (!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
         funcs.push({ description: "Toggle Single Line", event: () => this.layoutDoc._singleLine = !this.layoutDoc._singleLine, icon: "expand-arrows-alt" });
-
-        const highlighting: ContextMenuProps[] = [];
-        ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
-            highlighting.push({
-                description: (FormattedTextBox._highlights.indexOf(option) === -1 ? "Highlight " : "Unhighlight ") + option, event: () => {
-                    e.stopPropagation();
-                    if (FormattedTextBox._highlights.indexOf(option) === -1) {
-                        FormattedTextBox._highlights.push(option);
-                    } else {
-                        FormattedTextBox._highlights.splice(FormattedTextBox._highlights.indexOf(option), 1);
-                    }
-                    this.updateHighlights();
-                }, icon: "expand-arrows-alt"
-            }));
-        funcs.push({ description: "highlighting...", subitems: highlighting, icon: "hand-point-right" });
-
+        funcs.push({ description: (!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
         ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
         this._downX = this._downY = Number.NaN;
     }
-- 
cgit v1.2.3-70-g09d2


From 866561aa20aff0f570d31edf64eae36cd2d35279 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 14 Jul 2020 17:27:19 -0400
Subject: fixed onClickHandler stuff to use functions properly.  fixed several
 linking to annotations/pushpins problems.

---
 src/client/documents/Documents.ts                  |  8 +-
 src/client/util/DocumentManager.ts                 |  4 +-
 src/client/util/LinkManager.ts                     | 11 ++-
 src/client/util/SettingsManager.tsx                |  4 +-
 src/client/views/DocumentButtonBar.tsx             |  2 +-
 src/client/views/MetadataEntryMenu.tsx             | 96 ++++++----------------
 src/client/views/collections/CollectionView.tsx    |  7 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 10 +--
 .../collectionFreeForm/FormatShapePane.tsx         |  2 +-
 .../collectionFreeForm/InkOptionsMenu.tsx          |  2 +-
 .../collectionGrid/CollectionGridView.tsx          |  2 +-
 .../CollectionMulticolumnView.tsx                  |  5 +-
 .../CollectionMultirowView.tsx                     |  4 +-
 src/client/views/linking/LinkMenuGroup.tsx         |  4 +-
 src/client/views/linking/LinkMenuItem.tsx          |  5 +-
 src/client/views/nodes/DocumentLinksButton.tsx     |  3 -
 src/client/views/nodes/DocumentView.tsx            | 17 ++--
 src/client/views/nodes/FieldView.tsx               |  2 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     |  2 +-
 src/fields/documentSchemas.ts                      |  3 +
 20 files changed, 78 insertions(+), 115 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/FormatShapePane.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 90cef31d9..1fd533b62 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -102,6 +102,8 @@ export interface DocumentOptions {
     childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox or Buxton layout in tree view)
     childLayoutString?: string; // template string for collection to use to render its children
     hideFilterView?: boolean; // whether to hide the filter popout on collections
+    hideLinkButton?: boolean; // whether the blue link counter button should be hidden
+    hideAllLinks?: boolean; // whether all individual blue anchor dots should be hidden
     _columnsHideIfEmpty?: boolean; // whether stacking view column headings should be hidden
     isTemplateForField?: string; // the field key for which the containing document is a rendering template
     isTemplateDoc?: boolean;
@@ -137,7 +139,7 @@ export interface DocumentOptions {
     lookupField?: ScriptField; // script that returns the value of a field. This script is passed the rootDoc, layoutDoc, field, and container of the document.  see PresBox.
     "onDoubleClick-rawScript"?: string; // onDoubleClick script in raw text form
     "onChildDoubleClick-rawScript"?: string; // onChildDoubleClick script in raw text form
-    "onChildClick-rawScript"?: string // on ChildClick script in raw text form
+    "onChildClick-rawScript"?: string; // on ChildClick script in raw text form
     "onClick-rawScript"?: string; // onClick script in raw text form
     "onCheckedClick-rawScript"?: string; // onChecked script in raw text form
     "onCheckedClick-params"?: List<string>; // parameter list for onChecked treeview functions
@@ -779,7 +781,7 @@ export namespace Docs {
 
 
         export function FontIconDocument(options?: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.FONTICON), undefined, { ...(options || {}) });
+            return InstanceFromProto(Prototypes.get(DocumentType.FONTICON), undefined, { hideLinkButton: true, ...(options || {}) });
         }
 
         export function PresElementBoxDocument(options?: DocumentOptions) {
@@ -912,6 +914,8 @@ export namespace DocUtils {
         if (target.doc === Doc.UserDoc()) return undefined;
 
         const linkDoc = Docs.Create.LinkDocument(source, target, { linkRelationship, layoutKey: "layout_linkView", description }, id);
+        linkDoc.linkDisplay = true;
+        linkDoc.hideAnhors = true;
         linkDoc.layout_linkView = Cast(Cast(Doc.UserDoc()["template-button-link"], Doc, null).dragFactory, Doc, null);
         Doc.GetProto(linkDoc).title = ComputedField.MakeFunction('self.anchor1?.title +" (" + (self.linkRelationship||"to") +") "  + self.anchor2?.title');
 
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 1fa5faeb3..b66e7fdc4 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -146,7 +146,7 @@ export class DocumentManager {
         };
         const docView = getFirstDocView(targetDoc, originatingDoc);
         let annotatedDoc = await Cast(targetDoc.annotationOn, Doc);
-        if (annotatedDoc && !linkDoc?.isPushpin) {
+        if (annotatedDoc && !targetDoc?.isPushpin) {
             const first = getFirstDocView(annotatedDoc);
             if (first) {
                 annotatedDoc = first.props.Document;
@@ -156,7 +156,7 @@ export class DocumentManager {
             }
         }
         if (docView) {  // we have a docView already and aren't forced to create a new one ... just focus on the document.  TODO move into view if necessary otherwise just highlight?
-            if (linkDoc?.isPushpin) docView.props.Document.hidden = !docView.props.Document.hidden;
+            if (originatingDoc?.isPushpin) docView.props.Document.hidden = !docView.props.Document.hidden;
             else {
                 docView.props.Document.hidden && (docView.props.Document.hidden = undefined);
                 docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish);
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 50f3fc1d6..974744344 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -63,12 +63,17 @@ export class LinkManager {
     }
 
     // finds all links that contain the given anchor
-    public getAllRelatedLinks(anchor: Doc): Doc[] {
+    public getAllDirectLinks(anchor: Doc): Doc[] {
         const related = LinkManager.Instance.getAllLinks().filter(link => {
             const protomatch1 = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, null));
             const protomatch2 = Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, null));
             return protomatch1 || protomatch2 || Doc.AreProtosEqual(link, anchor);
         });
+        return related;
+    }
+    // finds all links that contain the given anchor
+    public getAllRelatedLinks(anchor: Doc): Doc[] {
+        const related = LinkManager.Instance.getAllDirectLinks(anchor);
         DocListCast(anchor[Doc.LayoutFieldKey(anchor) + "-annotations"]).map(anno => {
             related.push(...LinkManager.Instance.getAllRelatedLinks(anno));
         });
@@ -208,4 +213,6 @@ export class LinkManager {
 }
 
 Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); },
-    "creates a link to inputted document", "(doc: any)");
\ No newline at end of file
+    "returns all the links to the document or its annotations", "(doc: any)");
+Scripting.addGlobal(function directLinks(doc: any) { return new List(LinkManager.Instance.getAllDirectLinks(doc)); },
+    "returns all the links directly to the document", "(doc: any)");
\ No newline at end of file
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index f7ca3942b..981ee698d 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -87,11 +87,11 @@ export default class SettingsManager extends React.Component<{}> {
     }
     @action
     googleAuthorize = (event: any) => {
-        GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true)
+        GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true);
     }
     @action
     hypothesisAuthorize = (event: any) => {
-        HypothesisAuthenticationManager.Instance.fetchAccessToken(true)
+        HypothesisAuthenticationManager.Instance.fetchAccessToken(true);
     }
 
     private get settingsInterface() {
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index c188618f4..927c192cd 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -208,7 +208,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         const view0 = this.view0;
         return !view0 ? (null) : <Tooltip title="Show metadata panel"><div className="documentButtonBar-linkFlyout">
             <Flyout anchorPoint={anchorPoints.LEFT_TOP}
-                content={<MetadataEntryMenu docs={() => this.props.views().filter(dv => dv).map(dv => dv!.props.Document)} suggestWithFunction />  /* tfs: @bcz This might need to be the data document? */}>
+                content={<MetadataEntryMenu docs={this.props.views().filter(dv => dv).map(dv => dv!.props.Document)} suggestWithFunction />  /* tfs: @bcz This might need to be the data document? */}>
                 <div className={"documentButtonBar-linkButton-" + "empty"} onPointerDown={e => e.stopPropagation()} >
                     {<FontAwesomeIcon className="documentdecorations-icon" icon="tag" size="sm" />}
                 </div>
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index b0752ffb2..ca8a6e1d7 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -3,14 +3,14 @@ import "./MetadataEntryMenu.scss";
 import { observer } from 'mobx-react';
 import { observable, action, runInAction, trace, computed, IReactionDisposer, reaction } from 'mobx';
 import { KeyValueBox } from './nodes/KeyValueBox';
-import { Doc, Field, DocListCastAsync } from '../../fields/Doc';
+import { Doc, Field, DocListCastAsync, DocListCast } from '../../fields/Doc';
 import * as Autosuggest from 'react-autosuggest';
-import { undoBatch } from '../util/UndoManager';
+import { undoBatch, UndoManager } from '../util/UndoManager';
 import { emptyFunction, emptyPath } from '../../Utils';
 
 export type DocLike = Doc | Doc[] | Promise<Doc> | Promise<Doc[]>;
 export interface MetadataEntryProps {
-    docs: DocLike | (() => DocLike);
+    docs: Doc[];
     onError?: () => boolean;
     suggestWithFunction?: boolean;
 }
@@ -39,26 +39,13 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
         let onProto: boolean = false;
         let value: string | undefined = undefined;
         let docs = this.props.docs;
-        if (typeof docs === "function") {
-            if (this.props.suggestWithFunction) {
-                docs = docs();
-            } else {
-                return;
-            }
-        }
-        docs = await docs;
-        if (docs instanceof Doc) {
-            await docs[this._currentKey];
-            value = Field.toKeyValueString(docs, this._currentKey);
-        } else {
-            for (const doc of docs) {
-                const v = await doc[this._currentKey];
-                onProto = onProto || !Object.keys(doc).includes(this._currentKey);
-                if (field === null) {
-                    field = v;
-                } else if (v !== field) {
-                    value = "multiple values";
-                }
+        for (const doc of docs) {
+            const v = await doc[this._currentKey];
+            onProto = onProto || !Object.keys(doc).includes(this._currentKey);
+            if (field === null) {
+                field = v;
+            } else if (v !== field) {
+                value = "multiple values";
             }
         }
         if (value === undefined) {
@@ -86,27 +73,16 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
             const script = KeyValueBox.CompileKVPScript(this._currentValue);
             if (!script) return;
 
-            let doc = this.props.docs;
-            if (typeof doc === "function") {
-                doc = doc();
-            }
-            doc = await doc;
-
-            let success: boolean;
-            if (doc instanceof Doc) {
-                success = KeyValueBox.ApplyKVPScript(doc, this._currentKey, script);
-            } else {
-                let childSuccess = true;
-                if (this._addChildren) {
-                    for (const document of doc) {
-                        const collectionChildren = await DocListCastAsync(document.data);
-                        if (collectionChildren) {
-                            childSuccess = collectionChildren.every(c => KeyValueBox.ApplyKVPScript(c, this._currentKey, script));
-                        }
+            let childSuccess = true;
+            if (this._addChildren) {
+                for (const document of this.props.docs) {
+                    const collectionChildren = DocListCast(document.data);
+                    if (collectionChildren) {
+                        childSuccess = collectionChildren.every(c => KeyValueBox.ApplyKVPScript(c, this._currentKey, script));
                     }
                 }
-                success = doc.every(d => KeyValueBox.ApplyKVPScript(d, this._currentKey, script)) && childSuccess;
             }
+            const success = this.props.docs.every(d => KeyValueBox.ApplyKVPScript(d, this._currentKey, script)) && childSuccess;
             if (!success) {
                 if (this.props.onError) {
                     if (this.props.onError()) {
@@ -132,24 +108,12 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
         }
     }
 
-    getKeySuggestions = async (value: string): Promise<string[]> => {
+    getKeySuggestions = (value: string) => {
         value = value.toLowerCase();
         let docs = this.props.docs;
-        if (typeof docs === "function") {
-            if (this.props.suggestWithFunction) {
-                docs = docs();
-            } else {
-                return [];
-            }
-        }
-        docs = await docs;
-        if (docs instanceof Doc) {
-            return Object.keys(docs).filter(key => key.toLowerCase().startsWith(value));
-        } else {
-            const keys = new Set<string>();
-            docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
-            return Array.from(keys).filter(key => key.toLowerCase().startsWith(value));
-        }
+        const keys = new Set<string>();
+        docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
+        return Array.from(keys).filter(key => key.toLowerCase().startsWith(value));
     }
     getSuggestionValue = (suggestion: string) => suggestion;
 
@@ -157,9 +121,8 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
         return (null);
     }
     componentDidMount() {
-
         this._suggestionDispser = reaction(() => this._currentKey,
-            () => this.getKeySuggestions(this._currentKey).then(action((s: string[]) => this._allSuggestions = s)),
+            () => this._allSuggestions = this.getKeySuggestions(this._currentKey),
             { fireImmediately: true });
     }
     componentWillUnmount() {
@@ -171,19 +134,8 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
     }
 
     private get considerChildOptions() {
-        let docSource = this.props.docs;
-        if (typeof docSource === "function") {
-            docSource = docSource();
-        }
-        docSource = docSource as Doc[] | Doc;
-        if (docSource instanceof Doc) {
-            if (docSource._viewType === undefined) {
-                return (null);
-            }
-        } else if (Array.isArray(docSource)) {
-            if (!docSource.every(doc => doc._viewType !== undefined)) {
-                return null;
-            }
+        if (!this.props.docs.every(doc => doc._viewType !== undefined)) {
+            return null;
         }
         return (
             <div style={{ display: "flex" }}>
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 5165a8f11..6ab4645ea 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -140,17 +140,18 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             } else {
                 added.map(doc => {
                     const context = Cast(doc.context, Doc, null);
-                    if (context && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG) &&
-                        !DocListCast(doc.links).some(d => d.isPushpin)) {
+                    if (context && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) {
                         const pushpin = Docs.Create.FontIconDocument({
                             icon: "map-pin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null), _backgroundColor: "#0000003d", color: "#ACCEF7",
                             _width: 15, _height: 15, _xPadding: 0, isLinkButton: true, displayTimecode: Cast(doc.displayTimecode, "number", null)
                         });
+                        pushpin.isPushpin = true;
+                        Doc.GetProto(pushpin).annotationOn = doc.annotationOn;
+                        Doc.SetInPlace(doc, "annotationOn", undefined, true);
                         Doc.AddDocToList(context, Doc.LayoutFieldKey(context) + "-annotations", pushpin);
                         const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin", "");
                         const first = DocListCast(pushpin.links).find(d => d instanceof Doc);
                         first && (first.hidden = true);
-                        pushpinLink && (Doc.GetProto(pushpinLink).isPushpin = true);
                         doc.displayTimecode = undefined;
                     }
                     doc.context = this.props.Document;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f33b5371f..994f4ce6e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -939,9 +939,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     }
 
     @computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; }
-    @computed get onChildClickHandler() { return () => (this.props.childClickScript || ScriptCast(this.Document.onChildClick)); }
-    @computed get onChildDoubleClickHandler() { return () => (this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick)); }
     @computed get backgroundActive() { return this.layoutDoc.isBackground && (this.props.ContainingCollectionView?.active() || this.props.active()); }
+    onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
+    onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
     backgroundHalo = () => BoolCast(this.Document.useClusters);
     parentActive = (outsideReaction: boolean) => this.props.active(outsideReaction) || this.backgroundActive ? true : false;
     getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
@@ -1186,8 +1186,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             const dragY = e.detail.clientY;
             const bounds = this._marqueeRef.current?.getBoundingClientRect();
 
-            let deltaX = dragX - bounds.left < 25 ? -2 : bounds.right - dragX < 25 ? 2 : 0;
-            let deltaY = dragY - bounds.top < 25 ? -2 : bounds.bottom - dragY < 25 ? 2 : 0;
+            const deltaX = dragX - bounds.left < 25 ? -2 : bounds.right - dragX < 25 ? 2 : 0;
+            const deltaY = dragY - bounds.top < 25 ? -2 : bounds.bottom - dragY < 25 ? 2 : 0;
             (deltaX !== 0 || deltaY !== 0) && this.continuePan(deltaX, deltaY);
         }
         e.stopPropagation();
@@ -1198,7 +1198,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             const dragY = this._lastClientY;
             const dragX = this._lastClientX;
             if (dragY !== undefined && dragX !== undefined && this._marqueeRef.current) {
-                const bounds = this._marqueeRef.current.getBoundingClientRect()!;
+                const bounds = this._marqueeRef.current.getBoundingClientRect();
                 this.Document._panY = NumCast(this.Document._panY) + deltaY;
                 this.Document._panX = NumCast(this.Document._panX) + deltaX;
                 if (dragY - bounds.top < 25 || bounds.bottom - dragY < 25 || dragX - bounds.left < 25 || bounds.right - dragX < 25) {
diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
index 9d9ce7f36..4e328d838 100644
--- a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
+++ b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
@@ -31,7 +31,7 @@ export default class FormatShapePane extends AntimodeMenu {
 
     getField(key: string) {
         return this.selectedInk?.reduce((p, i) =>
-            (p === undefined || (p && p === i.rootDoc[key])) && i.rootDoc[key] !== "0" ? Field.toString(i.rootDoc[key] as Field) : "", undefined as Opt<string>)
+            (p === undefined || (p && p === i.rootDoc[key])) && i.rootDoc[key] !== "0" ? Field.toString(i.rootDoc[key] as Field) : "", undefined as Opt<string>);
     }
 
     @computed get selectedInk() {
diff --git a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
index 15707ad9e..5d115df69 100644
--- a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
@@ -128,7 +128,7 @@ export default class InkOptionsMenu extends AntimodeMenu {
                         // doc.strokeBezier === 300 ? doc.strokeBezier = 0 : doc.strokeBezier = 300;
                         break;
                     case "dash":
-                        doc.strokeDash = Number(value);
+                        doc.strokeDash = value;
                     default:
                         break;
                 }
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index b2e506dfa..73d0ae374 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -31,7 +31,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
     @observable private _rowHeight: Opt<number>; // temporary store of row height to make change undoable
     @observable private _scroll: number = 0; // required to make sure the decorations box container updates on scroll
 
-    @computed get onChildClickHandler() { return () => ScriptCast(this.Document.onChildClick); }
+    onChildClickHandler = () => ScriptCast(this.Document.onChildClick);
 
     @computed get numCols() { return NumCast(this.props.Document.gridNumCols, 10); }
     @computed get rowHeight() { return this._rowHeight === undefined ? NumCast(this.props.Document.gridRowHeight, 100) : this._rowHeight; }
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 402e7563d..21d283547 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -202,9 +202,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
     }
 
 
-    @computed get onChildClickHandler() { return () => ScriptCast(this.Document.onChildClick); }
-    @computed get onChildDoubleClickHandler() { return () => ScriptCast(this.Document.onChildDoubleClick); }
-
+    onChildClickHandler = () => ScriptCast(this.Document.onChildClick);
+    onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick);
 
     addDocTab = (doc: Doc, where: string) => {
         if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index e4ef9b436..d02088a6c 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -202,8 +202,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
     }
 
 
-    @computed get onChildClickHandler() { return () => ScriptCast(this.Document.onChildClick); }
-    @computed get onChildDoubleClickHandler() { return () => ScriptCast(this.Document.onChildDoubleClick); }
+    onChildClickHandler = () => ScriptCast(this.Document.onChildClick);
+    onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick);
 
     addDocTab = (doc: Doc, where: string) => {
         if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index 2f6b75437..2ae87ac13 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -11,6 +11,7 @@ import { DocumentView } from "../nodes/DocumentView";
 import './LinkMenu.scss';
 import { LinkMenuItem, StartLinkTargetsDrag } from "./LinkMenuItem";
 import React = require("react");
+import { Cast } from "../../../fields/Types";
 
 interface LinkMenuGroupProps {
     sourceDoc: Doc;
@@ -66,7 +67,8 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
 
     render() {
         const groupItems = this.props.group.map(linkDoc => {
-            const destination = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc);
+            const destination = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc) ||
+                LinkManager.Instance.getOppositeAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, null).annotationOn === this.props.sourceDoc ? Cast(linkDoc.anchor2, Doc, null) : Cast(linkDoc.anchor1, Doc, null));
             if (destination && this.props.sourceDoc) {
                 return <LinkMenuItem key={destination[Id] + this.props.sourceDoc[Id]}
                     groupType={this.props.groupType}
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index b451f0168..b92da6644 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -1,6 +1,6 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faArrowRight, faChevronDown, faChevronUp, faEdit, faEye, faTimes, faPencilAlt, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { FontAwesomeIcon, FontAwesomeIconProps } from '@fortawesome/react-fontawesome';
 import { action, observable } from 'mobx';
 import { observer } from "mobx-react";
 import { Doc, DocListCast } from '../../../fields/Doc';
@@ -189,7 +189,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
 
         const eyeIcon = this.props.linkDoc.hidden ? "eye-slash" : "eye";
 
-        let destinationIcon: string = "";;
+        let destinationIcon: FontAwesomeIconProps["icon"] = "question";
         switch (this.props.destinationDoc.type) {
             case DocumentType.IMG: destinationIcon = "image"; break;
             case DocumentType.COMPARISON: destinationIcon = "columns"; break;
@@ -205,7 +205,6 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
             case DocumentType.SCRIPTING: destinationIcon = "terminal"; break;
             case DocumentType.IMPORT: destinationIcon = "cloud-upload-alt"; break;
             case DocumentType.DOCHOLDER: destinationIcon = "expand"; break;
-            default: "question";
         }
 
         const title = StrCast(this.props.destinationDoc.title).length > 18 ?
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 83710cfbf..823e25471 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -101,8 +101,6 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                     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");
                         LinkManager.currentLink = linkDoc;
-                        linkDoc ? linkDoc.hidden = true : null;
-                        linkDoc ? linkDoc.linkDisplay = true : null;
 
                         runInAction(() => {
                             if (linkDoc) {
@@ -137,7 +135,6 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                     const linkDoc = DocUtils.MakeLink({ doc: DocumentLinksButton.StartLink.props.Document }, { doc: this.props.View.props.Document }, "long drag");
                     LinkManager.currentLink = linkDoc;
                     linkDoc ? linkDoc.hidden = true : null;
-                    linkDoc ? linkDoc.linkDisplay = true : null;
 
                     runInAction(() => {
                         if (linkDoc) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7d9c4da87..a6771443a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -128,9 +128,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth, this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
     @computed get nativeHeight() { return NumCast(this.layoutDoc._nativeHeight, this.props.NativeHeight() || (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); }
     @computed get onClickHandler() { return this.props.onClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null)); }
-    @computed get onDoubleClickHandler() { return this.props.onDoubleClick?.() || (Cast(this.layoutDoc.onDoubleClick, ScriptField, null) || this.Document.onDoubleClick); }
-    @computed get onPointerDownHandler() { return this.props.onPointerDown?.() || ScriptCast(this.Document.onPointerDown) }
-    @computed get onPointerUpHandler() { return this.props.onPointerUp?.() || ScriptCast(this.Document.onPointerUp); }
+    @computed get onDoubleClickHandler() { return this.props.onDoubleClick?.() ?? (Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick); }
+    @computed get onPointerDownHandler() { return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown); }
+    @computed get onPointerUpHandler() { return this.props.onPointerUp?.() ?? ScriptCast(this.Document.onPointerUp); }
     NativeWidth = () => this.nativeWidth;
     NativeHeight = () => this.nativeHeight;
     onClickFunc = () => this.onClickHandler;
@@ -298,7 +298,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             if (this._doubleTap && this.props.renderDepth && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
                 if (!(e.nativeEvent as any).formattedHandled) {
                     if (this.onDoubleClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself
-                        const func = () => this.onDoubleClickHandler?.script.run({
+                        const func = () => this.onDoubleClickHandler.script.run({
                             this: this.layoutDoc,
                             self: this.rootDoc,
                             thisContainer: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey
@@ -320,7 +320,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 }
             } else if (this.onClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself
                 //SelectionManager.DeselectAll();
-                const func = () => this.onClickHandler?.script.run({
+                const func = () => this.onClickHandler.script.run({
                     this: this.layoutDoc,
                     self: this.rootDoc,
                     thisContainer: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey
@@ -556,7 +556,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         this.cleanUpInteractions();
 
         if (this.onPointerUpHandler?.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
-            this.onPointerUpHandler?.script.run({ self: this.rootDoc, this: this.layoutDoc }, console.log);
+            this.onPointerUpHandler.script.run({ self: this.rootDoc, this: this.layoutDoc }, console.log);
             document.removeEventListener("pointerup", this.onPointerUp);
             return;
         }
@@ -644,7 +644,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const makeLink = action((linkDoc: Doc) => {
             LinkManager.currentLink = linkDoc;
             linkDoc.hidden = true;
-            linkDoc.linkDisplay = true;
 
             LinkCreatedBox.popupX = de.x;
             LinkCreatedBox.popupY = de.y - 33;
@@ -1076,7 +1075,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 layoutKey={this.finalLayoutKey} />
             {this.layoutDoc.hideAllLinks ? (null) : this.allAnchors}
             {/* {this.allAnchors} */}
-            {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.props.dontRegisterView ? (null) : <DocumentLinksButton View={this} Offset={[-15, 0]} />}
+            {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.layoutDoc.hideLinkButton || this.props.dontRegisterView ? (null) : <DocumentLinksButton View={this} Offset={[-15, 0]} />}
         </div>
         );
     }
@@ -1105,7 +1104,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         return (this.props.treeViewDoc && this.props.LayoutTemplateString) || // render nothing for: tree view anchor dots
             this.layoutDoc.presBox ||  // presentationbox nodes
             this.props.dontRegisterView ? (null) : // view that are not registered
-            DocUtils.FilterDocs(DocListCast(this.Document.links), this.props.docFilters(), []).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) =>
+            DocUtils.FilterDocs(LinkManager.Instance.getAllDirectLinks(this.Document), this.props.docFilters(), []).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) =>
                 <DocumentView {...this.props} key={i + 1}
                     Document={d}
                     ContainingCollectionView={this.props.ContainingCollectionView}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index c57738361..48e1f6ce3 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -25,7 +25,7 @@ export interface FieldViewProps {
     Document: Doc;
     DataDoc?: Doc;
     LibraryPath: Doc[];
-    onClick?: ScriptField;
+    onClick?: () => ScriptField;
     dropAction: dropActionType;
     backgroundHalo?: () => boolean;
     docFilters: () => string[];
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 8da1f99b5..c8798d757 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -431,7 +431,7 @@ export default class RichTextMenu extends AntimodeMenu {
         const nextIsOL = this.view.state.selection.$from.nodeAfter?.type === schema.nodes.ordered_list;
         let inList: any = undefined;
         let fromList = -1;
-        let path: any = Array.from((this.view.state.selection.$from as any).path);
+        const path: any = Array.from((this.view.state.selection.$from as any).path);
         for (let i = 0; i < path.length; i++) {
             if (path[i]?.type === schema.nodes.ordered_list) {
                 inList = path[i];
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 9dda644f3..ddffb56c3 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -65,6 +65,7 @@ export const documentSchema = createSchema({
     color: "string",            // foreground color of document
     fitToBox: "boolean",        // whether freeform view contents should be zoomed/panned to fill the area of the document view
     fontSize: "string",
+    hidden: "boolean",          // whether a document should not be displayed
     isInkMask: "boolean",       // is the document a mask (ie, sits on top of other documents, has an unbounded width/height that is dark, and content uses 'hard-light' mix-blend-mode to let other documents pop through)
     layout: "string",           // this is the native layout string for the document.  templates can be added using other fields and setting layoutKey below
     layoutKey: "string",        // holds the field key for the field that actually holds the current lyoat
@@ -87,6 +88,8 @@ export const documentSchema = createSchema({
     onPointerUp: ScriptField,   // script to run when document is clicked (can be overriden by an onClick prop)
     onDragStart: ScriptField,   // script to run when document is dragged (without being selected).  the script should return the Doc to be dropped.
     followLinkLocation: "string",// flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab, ) 
+    hideLinkButton: "boolean",  // whether the blue link counter button should be hidden
+    hideAllLinks: "boolean",    // whether all individual blue anchor dots should be hidden
     isInPlaceContainer: "boolean",// whether the marked object will display addDocTab() calls that target "inPlace" destinations
     isLinkButton: "boolean",    // whether document functions as a link follow button to follow the first link on the document when clicked   
     isBackground: "boolean",    // whether document is a background element and ignores input events (can only select with marquee)
-- 
cgit v1.2.3-70-g09d2

