From dd9eda99662d1bfb921a8f1589c5f77ac9321172 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 21 Jun 2020 03:00:43 -0400
Subject: cleaned up lists in prosemirror.  everything's an ordered_list now
 (even unordred lists).   things are read back from the dom correctly to allow
 copy & paste.  added prosemirror's dev tools.

---
 .../views/nodes/formattedText/RichTextMenu.tsx     | 32 ++++++++++------------
 1 file changed, 15 insertions(+), 17 deletions(-)

(limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')

diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 03d393cde..456ac7770 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -189,9 +189,9 @@ export default class RichTextMenu extends AntimodeMenu {
             const node = (state.selection as NodeSelection).node;
             if (node?.type === schema.nodes.ordered_list) {
                 let attrs = node.attrs;
-                if (mark.type === schema.marks.pFontFamily) attrs = { ...attrs, setFontFamily: mark.attrs.family };
-                if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, setFontSize: mark.attrs.fontSize };
-                if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, setFontColor: mark.attrs.color };
+                if (mark.type === schema.marks.pFontFamily) attrs = { ...attrs, fontFamily: mark.attrs.family };
+                if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, fontSize: `${mark.attrs.fontSize}px` };
+                if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, fontColor: mark.attrs.color };
                 const tr = updateBullets(state.tr.setNodeMarkup(state.selection.from, node.type, attrs), state.schema);
                 dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(state.selection.from))));
             } else {
@@ -378,22 +378,20 @@ export default class RichTextMenu extends AntimodeMenu {
 
     // TODO: remove doesn't work
     //remove all node type and apply the passed-in one to the selected text
-    changeListType = (nodeType: NodeType | undefined) => {
+    changeListType = (nodeType: Node | undefined) => {
         if (!this.view) return;
 
-        if (nodeType === schema.nodes.bullet_list) {
-            wrapInList(nodeType)(this.view.state, this.view.dispatch);
-        } else {
-            const marks = this.view.state.storedMarks || (this.view.state.selection.$to.parentOffset && this.view.state.selection.$from.marks());
-            if (!wrapInList(schema.nodes.ordered_list)(this.view.state, (tx2: any) => {
-                const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle);
-                marks && tx3.ensureMarks([...marks]);
-                marks && tx3.setStoredMarks([...marks]);
-
-                this.view!.dispatch(tx2);
-            })) {
-                const tx2 = this.view.state.tr;
-                const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle);
+        const marks = this.view.state.storedMarks || (this.view.state.selection.$to.parentOffset && this.view.state.selection.$from.marks());
+        if (!wrapInList(schema.nodes.ordered_list)(this.view.state, (tx2: any) => {
+            const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle, this.view!.state.selection.from - 1, this.view!.state.selection.to + 1);
+            marks && tx3.ensureMarks([...marks]);
+            marks && tx3.setStoredMarks([...marks]);
+
+            this.view!.dispatch(tx2);
+        })) {
+            const tx2 = this.view.state.tr;
+            if (nodeType && this.view.state.selection.$from.nodeAfter?.type === schema.nodes.ordered_list) {
+                const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle, this.view.state.selection.from, this.view.state.selection.to);
                 marks && tx3.ensureMarks([...marks]);
                 marks && tx3.setStoredMarks([...marks]);
 
-- 
cgit v1.2.3-70-g09d2


From bc8672634f8ec2fa0d60bca3fb01a6943132f99c Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 21 Jun 2020 11:14:25 -0400
Subject: more cleanup of prosemirror bullets and ordered_list mode changing

---
 src/client/views/nodes/formattedText/FormattedTextBox.scss         | 7 +++++--
 src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts | 2 +-
 src/client/views/nodes/formattedText/RichTextMenu.tsx              | 4 ++--
 3 files changed, 8 insertions(+), 5 deletions(-)

(limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')

diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 173befdc1..348ed4ba5 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -296,7 +296,8 @@ footnote::after {
         font-family: inherit;
     }
     .bullet {  p {display: inline; font-family: inherit} margin-left: 0; }
-    .bullet1,.bullet2,.bullet3,.bullet4,.bullet5,.bullet6 { p {display: inline; font-family: inherit} font-size: smaller; }
+    .bullet1 { p {display: inline; font-family: inherit} }
+    .bullet2,.bullet3,.bullet4,.bullet5,.bullet6 { p {display: inline; font-family: inherit} font-size: smaller; }
 
     .decimal1-ol { counter-reset: deci1; p {display: inline; font-family: inherit} margin-left: 0; }
     .decimal2-ol { counter-reset: deci2; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 1em;}
@@ -310,6 +311,8 @@ footnote::after {
     .multi2-ol { counter-reset: multi2; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 1.4em;}
     .multi3-ol { counter-reset: multi3; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 2em;}
     .multi4-ol { counter-reset: multi4; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 3.4em;}
+    
+    .bullet:before, .bullet1:before, .bullet2:before, .bullet3:before, .bullet4:before, .bullet5:before { transition: 0.5s; display: inline-block; margin-left: -1em; width: 1em; content:" " }
 
     .decimal1:before { transition: 0.5s;counter-increment: deci1; display: inline-block; margin-left: -1em;   width:  1em;   content: counter(deci1) ". ";  }
     .decimal2:before { transition: 0.5s;counter-increment: deci2; display: inline-block; margin-left: -2.1em; width: 2.1em;  content: counter(deci1) "."counter(deci2) ". ";  }
@@ -322,7 +325,7 @@ footnote::after {
     .multi1:before { transition: 0.5s;counter-increment: multi1; display: inline-block; margin-left: -1em;    width: 1.2em;  content: counter(multi1, upper-alpha) ". ";  }
     .multi2:before { transition: 0.5s;counter-increment: multi2; display: inline-block; margin-left: -2em;    width:   2em;  content: counter(multi1, upper-alpha) "."counter(multi2, decimal) ". "; }
     .multi3:before { transition: 0.5s;counter-increment: multi3; display: inline-block; margin-left: -2.85em; width:2.85em;  content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) ". "; }
-    .multi4:before { transition: 0.5s;counter-increment: multi4; display: inline-block; margin-left:   -4.2em;  width: 4.2em;    content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) "."counter(multi4, lower-roman) ". "; }
+    .multi4:before { transition: 0.5s;counter-increment: multi4; display: inline-block; margin-left: -4.2em;  width: 4.2em;    content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) "."counter(multi4, lower-roman) ". "; }
 }
 
 .formattedTextBox-inner-rounded-selected,
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 29bd1da67..75cfe6bd1 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -18,7 +18,7 @@ export type KeyMap = { [key: string]: any };
 
 export let updateBullets = (tx2: Transaction, schema: Schema, mapStyle?: string, from?: number, to?: number) => {
     tx2.doc.descendants((node: any, offset: any, index: any) => {
-        if ((!from || !to || (from <= offset && to >= offset)) && (node.type === schema.nodes.ordered_list || node.type === schema.nodes.list_item)) {
+        if ((from === undefined || to === undefined || (from <= offset + node.nodeSize && to >= offset)) && (node.type === schema.nodes.ordered_list || node.type === schema.nodes.list_item)) {
             const path = (tx2.doc.resolve(offset) as any).path;
             let depth = Array.from(path).reduce((p: number, c: any) => p + (c.hasOwnProperty("type") && c.type === schema.nodes.ordered_list ? 1 : 0), 0);
             if (node.type === schema.nodes.ordered_list) depth++;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 456ac7770..1e14b8237 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -104,7 +104,7 @@ export default class RichTextMenu extends AntimodeMenu {
             { node: schema.nodes.ordered_list.create({ mapStyle: "bullet" }), title: "Set list type", label: ":", command: this.changeListType },
             { node: schema.nodes.ordered_list.create({ mapStyle: "decimal" }), title: "Set list type", label: "1.1", command: this.changeListType },
             { node: schema.nodes.ordered_list.create({ mapStyle: "multi" }), title: "Set list type", label: "1.A", command: this.changeListType },
-            { node: undefined, title: "Set list type", label: "Remove", command: this.changeListType },
+            //{ node: undefined, title: "Set list type", label: "Remove", command: this.changeListType },
         ];
 
         this.fontColors = [
@@ -395,7 +395,7 @@ export default class RichTextMenu extends AntimodeMenu {
                 marks && tx3.ensureMarks([...marks]);
                 marks && tx3.setStoredMarks([...marks]);
 
-                this.view.dispatch(tx3);
+                this.view.dispatch(tx3.setSelection(new NodeSelection(tx3.doc.resolve(this.view.state.selection.$from.pos))));
             }
         }
     }
-- 
cgit v1.2.3-70-g09d2


From 477ba37f5728888e940119e4bc798a416565f960 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 23 Jun 2020 00:36:02 -0400
Subject: fixed mult link menu. fixed linking to external urls. fixed draggers
 for text menu.

---
 deploy/index.html                                  |  2 +-
 src/client/views/AntimodeMenu.tsx                  |  6 ++--
 .../collectionFreeForm/InkOptionsMenu.tsx          |  1 -
 .../nodes/formattedText/FormattedTextBox.scss      |  9 +++---
 .../views/nodes/formattedText/FormattedTextBox.tsx |  8 ++++--
 .../views/nodes/formattedText/RichTextMenu.tsx     | 32 +++++++++-------------
 6 files changed, 27 insertions(+), 31 deletions(-)

(limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')

diff --git a/deploy/index.html b/deploy/index.html
index be0658da2..fdfd77cc2 100644
--- a/deploy/index.html
+++ b/deploy/index.html
@@ -9,7 +9,7 @@
 </head>
 
 <body>
-    <script src="https://hypothes.is/embed.js" async></script>
+    <!-- <script src="https://hypothes.is/embed.js" async></script> -->
     <div id="root"></div>
     <script src="/bundle.js"></script>
 </body>
diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx
index 9a6121d20..2d26afcf6 100644
--- a/src/client/views/AntimodeMenu.tsx
+++ b/src/client/views/AntimodeMenu.tsx
@@ -131,15 +131,15 @@ export default abstract class AntimodeMenu extends React.Component {
     }
 
     protected getDragger = () => {
-        return <div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: this.Pinned ? "20px" : "0px" }} />;
+        return <div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: "20px" }} />;
     }
 
     protected getElement(buttons: JSX.Element[]) {
         return (
             <div className="antimodeMenu-cont" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
                 style={{ left: this._left, top: this._top, opacity: this._opacity, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay }}>
+                <div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: "20px" }} />
                 {buttons}
-                <div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: this.Pinned ? "20px" : "0px" }} />
             </div>
         );
     }
@@ -148,8 +148,8 @@ export default abstract class AntimodeMenu extends React.Component {
         return (
             <div className="antimodeMenu-cont with-rows" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
                 style={{ left: this._left, top: this._top, opacity: this._opacity, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay, height: "auto" }}>
+                {hasDragger ? <div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: "20px" }} /> : (null)}
                 {rows}
-                {hasDragger ? <div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: this.Pinned ? "20px" : "0px" }} /> : <></>}
             </div>
         );
     }
diff --git a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
index ae82c6a65..4fac87ca5 100644
--- a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
@@ -118,7 +118,6 @@ export default class InkOptionsMenu extends AntimodeMenu {
 
     render() {
         const buttons = [
-            <button className="antimodeMenu-button" title="Drag" key="drag" onPointerDown={e => this.dragStart(e)}>  ✜  </button>,
             ...this.shapeButtons,
             this.bezierButton,
             this.widthPicker,
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 348ed4ba5..ccdf41233 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -251,22 +251,23 @@ footnote::after {
 .prosemirror-links {
     display: none;
     position: absolute;
-    background-color: gray;
-    padding-bottom: 10px;
-    margin-top: 1em;
+    background-color: dimgray;
+    margin-top: 1.5em;
     z-index: 1;
+    padding: 5;
+    border-radius: 2px;
   }
   .prosemirror-hrefoptions{
       width:0px;
       border:unset;
       padding:0px;
-
   }
   
   .prosemirror-links a {
     float: left;
     color: white;
     text-decoration: none;
+    border-radius: 3px;
   }
   
   .prosemirror-links a:hover {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2a019ada7..3ee5603e5 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -599,10 +599,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         };
     }
 
-    makeLinkToSelection(linkId: string, title: string, location: string, targetId: string) {
+    makeLinkToSelection(linkId: string, title: string, location: string, targetId: string, targetHref?: string) {
         const state = this._editorView?.state;
         if (state) {
-            const href = Utils.prepend("/doc/" + linkId);
+            const href = targetHref ?? Utils.prepend("/doc/" + linkId);
             const sel = state.selection;
             const splitter = state.schema.marks.splitter.create({ id: Utils.GenerateGuid() });
             let tr = state.tr.addMark(sel.from, sel.to, splitter);
@@ -610,7 +610,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
                     const allHrefs = [{ href, title, targetId, linkId }];
                     allHrefs.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.link.name)?.attrs.allHrefs ?? []));
-                    const link = state.schema.marks.link.create({ href, allHrefs, title, location, linkId, targetId });
+                    const link = state.schema.marks.link.create({ allHrefs, title, location, linkId });
                     tr = tr.addMark(pos, pos + node.nodeSize, link);
                 }
             });
@@ -937,6 +937,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 const { state: { tr }, dispatch } = this._editorView;
                 dispatch(tr.insertText(startupText));
             }
+            (this._editorView as any).TextView = this;
         }
 
         const selectOnLoad = this.rootDoc[Id] === FormattedTextBox.SelectOnLoad;
@@ -1040,6 +1041,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         if (bounds && this.layoutDoc._chromeStatus !== "disabled") {
             const x = Math.min(Math.max(bounds.left, 0), window.innerWidth - RichTextMenu.Instance.width);
             let y = Math.min(Math.max(0, bounds.top - RichTextMenu.Instance.height - 50), window.innerHeight - RichTextMenu.Instance.height);
+            console.log("y = " + y + " hgt = " + RichTextMenu.Instance.height + " cords = " + coords.top);
             if (coords && coords.left > x && coords.left < x + RichTextMenu.Instance.width && coords.top > y && coords.top < y + RichTextMenu.Instance.height + 50) {
                 y = Math.min(bounds.bottom, window.innerHeight - RichTextMenu.Instance.height);
             }
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 1e14b8237..fecf2de85 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -12,7 +12,7 @@ import { faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSubscr
 import { updateBullets } from "./ProsemirrorExampleTransfer";
 import { FieldViewProps } from "../FieldView";
 import { Cast, StrCast } from "../../../../fields/Types";
-import { FormattedTextBoxProps } from "./FormattedTextBox";
+import { FormattedTextBoxProps, FormattedTextBox } from "./FormattedTextBox";
 import { unimplementedFunction, Utils } from "../../../../Utils";
 import { wrapInList } from "prosemirror-schema-list";
 import { PastelSchemaPalette, DarkPastelSchemaPalette } from '../../../../fields/SchemaHeaderField';
@@ -307,7 +307,7 @@ export default class RichTextMenu extends AntimodeMenu {
         function onClick(e: React.PointerEvent) {
             e.preventDefault();
             e.stopPropagation();
-            self.view && self.view.focus();
+            //self.view?.focus();
             self.view && command && command(self.view.state, self.view.dispatch, self.view);
             self.view && onclick && onclick(self.view.state, self.view.dispatch, self.view);
             self.setActiveMarkButtons(self.getActiveMarksOnSelection());
@@ -427,7 +427,7 @@ export default class RichTextMenu extends AntimodeMenu {
         function onBrushClick(e: React.PointerEvent) {
             e.preventDefault();
             e.stopPropagation();
-            self.view && self.view.focus();
+            // self.view?.focus();
             self.view && self.fillBrush(self.view.state, self.view.dispatch);
         }
 
@@ -501,13 +501,13 @@ export default class RichTextMenu extends AntimodeMenu {
         function onColorClick(e: React.PointerEvent) {
             e.preventDefault();
             e.stopPropagation();
-            self.view && self.view.focus();
+            // self.view?.focus();
             self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch);
         }
         function changeColor(e: React.PointerEvent, color: string) {
             e.preventDefault();
             e.stopPropagation();
-            self.view && self.view.focus();
+            //self.view?.focus();
             self.setActiveColor(color);
             self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch);
         }
@@ -554,13 +554,13 @@ export default class RichTextMenu extends AntimodeMenu {
         function onHighlightClick(e: React.PointerEvent) {
             e.preventDefault();
             e.stopPropagation();
-            self.view && self.view.focus();
+            // self.view?.focus();
             self.view && self.insertHighlight(self.activeHighlightColor, self.view.state, self.view.dispatch);
         }
         function changeHighlight(e: React.PointerEvent, color: string) {
             e.preventDefault();
             e.stopPropagation();
-            self.view && self.view.focus();
+            // self.view?.focus();
             self.setActiveHighlight(color);
             self.view && self.insertHighlight(self.activeHighlightColor, self.view.state, self.view.dispatch);
         }
@@ -659,15 +659,8 @@ export default class RichTextMenu extends AntimodeMenu {
     }
 
     // TODO: should check for valid URL
-    makeLinkToURL = (target: String, lcoation: string) => {
-        if (!this.view) return;
-
-        let node = this.view.state.selection.$from.nodeAfter;
-        let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target, location: location });
-        this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link));
-        this.view.dispatch(this.view.state.tr.addMark(this.view.state.selection.from, this.view.state.selection.to, link));
-        node = this.view.state.selection.$from.nodeAfter;
-        link = node && node.marks.find(m => m.type.name === "link");
+    makeLinkToURL = (target: string, lcoation: string) => {
+        ((this.view as any)?.TextView as FormattedTextBox).makeLinkToSelection("", target, "onRight", "", target);
     }
 
     deleteLink = () => {
@@ -760,13 +753,14 @@ export default class RichTextMenu extends AntimodeMenu {
         this.collapsed = !this.collapsed;
         setTimeout(() => {
             const x = Math.min(this._left, window.innerWidth - RichTextMenu.Instance.width);
-            RichTextMenu.Instance.jumpTo(x, this._top);
+            RichTextMenu.Instance.jumpTo(x, this._top, true);
         }, 0);
     }
 
     render() {
 
         const row1 = <div className="antimodeMenu-row" key="row1" style={{ display: this.collapsed ? "none" : undefined }}>{[
+            !this.collapsed ? this.getDragger() : (null),
             this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)),
             this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)),
             this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)),
@@ -781,6 +775,7 @@ export default class RichTextMenu extends AntimodeMenu {
         ]}</div>;
 
         const row2 = <div className="antimodeMenu-row row-2" key="antimodemenu row2">
+            {this.collapsed ? this.getDragger() : (null)}
             <div key="row" style={{ display: this.collapsed ? "none" : undefined }}>
                 {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size"),
                 this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family"),
@@ -795,7 +790,6 @@ export default class RichTextMenu extends AntimodeMenu {
                 <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>
-                {this.getDragger()}
             </div>
         </div>;
 
@@ -840,7 +834,7 @@ class ButtonDropdown extends React.Component<ButtonDropdownProps> {
     onDropdownClick = (e: React.PointerEvent) => {
         e.preventDefault();
         e.stopPropagation();
-        this.props.view && this.props.view.focus();
+        //this.props.view?.focus();
         this.toggleDropdown();
     }
 
-- 
cgit v1.2.3-70-g09d2


From 87150752e9ec0b984209a3e4fa757dfee18a788b Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 23 Jun 2020 00:36:49 -0400
Subject: from last

---
 src/client/views/nodes/formattedText/RichTextMenu.tsx | 7 -------
 1 file changed, 7 deletions(-)

(limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')

diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index fecf2de85..1a961ae21 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -307,7 +307,6 @@ export default class RichTextMenu extends AntimodeMenu {
         function onClick(e: React.PointerEvent) {
             e.preventDefault();
             e.stopPropagation();
-            //self.view?.focus();
             self.view && command && command(self.view.state, self.view.dispatch, self.view);
             self.view && onclick && onclick(self.view.state, self.view.dispatch, self.view);
             self.setActiveMarkButtons(self.getActiveMarksOnSelection());
@@ -427,7 +426,6 @@ export default class RichTextMenu extends AntimodeMenu {
         function onBrushClick(e: React.PointerEvent) {
             e.preventDefault();
             e.stopPropagation();
-            // self.view?.focus();
             self.view && self.fillBrush(self.view.state, self.view.dispatch);
         }
 
@@ -501,13 +499,11 @@ export default class RichTextMenu extends AntimodeMenu {
         function onColorClick(e: React.PointerEvent) {
             e.preventDefault();
             e.stopPropagation();
-            // self.view?.focus();
             self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch);
         }
         function changeColor(e: React.PointerEvent, color: string) {
             e.preventDefault();
             e.stopPropagation();
-            //self.view?.focus();
             self.setActiveColor(color);
             self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch);
         }
@@ -554,13 +550,11 @@ export default class RichTextMenu extends AntimodeMenu {
         function onHighlightClick(e: React.PointerEvent) {
             e.preventDefault();
             e.stopPropagation();
-            // self.view?.focus();
             self.view && self.insertHighlight(self.activeHighlightColor, self.view.state, self.view.dispatch);
         }
         function changeHighlight(e: React.PointerEvent, color: string) {
             e.preventDefault();
             e.stopPropagation();
-            // self.view?.focus();
             self.setActiveHighlight(color);
             self.view && self.insertHighlight(self.activeHighlightColor, self.view.state, self.view.dispatch);
         }
@@ -834,7 +828,6 @@ class ButtonDropdown extends React.Component<ButtonDropdownProps> {
     onDropdownClick = (e: React.PointerEvent) => {
         e.preventDefault();
         e.stopPropagation();
-        //this.props.view?.focus();
         this.toggleDropdown();
     }
 
-- 
cgit v1.2.3-70-g09d2


From 9e37056e3b1e5412e2eca9020841a56503f70d32 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 24 Jun 2020 01:46:42 -0400
Subject: changed richTextMenu to dock at top of window.  fixed ColorBox to set
 foreground color of selected text.

---
 deploy/index.html                                  |  2 +-
 src/client/views/AntimodeMenu.tsx                  |  6 ++++-
 src/client/views/MainView.tsx                      |  2 +-
 src/client/views/MetadataEntryMenu.tsx             | 26 +++++++++++++---------
 .../views/collections/CollectionDockingView.tsx    | 17 +++++++-------
 src/client/views/nodes/ColorBox.tsx                |  6 ++---
 .../views/nodes/formattedText/RichTextMenu.tsx     |  6 ++---
 7 files changed, 37 insertions(+), 28 deletions(-)

(limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')

diff --git a/deploy/index.html b/deploy/index.html
index fdfd77cc2..e441e3610 100644
--- a/deploy/index.html
+++ b/deploy/index.html
@@ -10,7 +10,7 @@
 
 <body>
     <!-- <script src="https://hypothes.is/embed.js" async></script> -->
-    <div id="root"></div>
+    <div id="root" style="position:relative"></div>
     <script src="/bundle.js"></script>
 </body>
 
diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx
index 2d26afcf6..3e4d20fea 100644
--- a/src/client/views/AntimodeMenu.tsx
+++ b/src/client/views/AntimodeMenu.tsx
@@ -147,7 +147,11 @@ export default abstract class AntimodeMenu extends React.Component {
     protected getElementWithRows(rows: JSX.Element[], numRows: number, hasDragger: boolean = true) {
         return (
             <div className="antimodeMenu-cont with-rows" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
-                style={{ left: this._left, top: this._top, opacity: this._opacity, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay, height: "auto" }}>
+                style={{
+                    left: this._left, top: this._top, opacity: this._opacity, transitionProperty: this._transitionProperty,
+                    transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay, height: "auto",
+                    flexDirection: this.Pinned ? "row" : undefined, position: this.Pinned ? "unset" : undefined
+                }}>
                 {hasDragger ? <div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: "20px" }} /> : (null)}
                 {rows}
             </div>
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index d6c46e3b0..0686e797e 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -570,6 +570,7 @@ export class MainView extends React.Component {
             <GoogleAuthenticationManager />
             <DocumentDecorations />
             <GestureOverlay>
+                <RichTextMenu key="rich" />
                 {this.mainContent}
             </GestureOverlay>
             <PreviewCursor />
@@ -578,7 +579,6 @@ export class MainView extends React.Component {
             <PDFMenu />
             <MarqueeOptionsMenu />
             <InkOptionsMenu />
-            <RichTextMenu />
             <OverlayView />
             <TimelineMenu />
             {this.snapLines}
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index e100d3f52..b0752ffb2 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -197,19 +197,23 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
     render() {
         return (<div className="metadataEntry-outerDiv" id="metadataEntry-outer" onPointerDown={e => e.stopPropagation()}>
             <div className="metadataEntry-inputArea">
-                Key:
-                <div className="metadataEntry-autoSuggester" onClick={e => this.autosuggestRef.current!.input?.focus()}  >
-                    <Autosuggest inputProps={{ value: this._currentKey, onChange: this.onKeyChange }}
-                        getSuggestionValue={this.getSuggestionValue}
-                        suggestions={emptyPath}
-                        alwaysRenderSuggestions={false}
-                        renderSuggestion={this.renderSuggestion}
-                        onSuggestionsFetchRequested={emptyFunction}
-                        onSuggestionsClearRequested={emptyFunction}
-                        ref={this.autosuggestRef} />
+                <div style={{ display: "flex", flexDirection: "row" }}>
+                    <span>Key:</span>
+                    <div className="metadataEntry-autoSuggester" onClick={e => this.autosuggestRef.current!.input?.focus()}  >
+                        <Autosuggest inputProps={{ value: this._currentKey, onChange: this.onKeyChange }}
+                            getSuggestionValue={this.getSuggestionValue}
+                            suggestions={emptyPath}
+                            alwaysRenderSuggestions={false}
+                            renderSuggestion={this.renderSuggestion}
+                            onSuggestionsFetchRequested={emptyFunction}
+                            onSuggestionsClearRequested={emptyFunction}
+                            ref={this.autosuggestRef} />
+                    </div>
                 </div>
-                Value:
+                <div style={{ display: "flex", flexDirection: "row" }}>
+                    <span>Value:</span>
                     <input className="metadataEntry-input" ref={this._ref} value={this._currentValue} onClick={e => this._ref.current!.focus()} onChange={this.onValueChange} onKeyDown={this.onValueKeyDown} />
+                </div>
                 {this.considerChildOptions}
             </div>
             <div className="metadataEntry-keys" >
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index a969e302d..ee987abdb 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -11,7 +11,7 @@ import { Id } from '../../../fields/FieldSymbols';
 import { FieldId } from "../../../fields/RefField";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnOne, returnTrue, Utils, returnZero, returnEmptyFilter } from "../../../Utils";
+import { emptyFunction, returnOne, returnTrue, Utils, returnZero, returnEmptyFilter, setupMoveUpEvents, returnFalse } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { Docs } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
@@ -521,13 +521,14 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                     tab.setActive(true);
                 };
                 const onDown = (e: React.PointerEvent) => {
-                    if (!(e.nativeEvent as any).defaultPrevented) {
-                        e.preventDefault();
-                        e.stopPropagation();
-                        const dragData = new DragManager.DocumentDragData([doc]);
-                        dragData.dropAction = doc.dropAction as dropActionType;
-                        DragManager.StartDocumentDrag([gearSpan], dragData, e.clientX, e.clientY);
-                    }
+                    setupMoveUpEvents(this, e, (e) => {
+                        if (!(e as any).defaultPrevented) {
+                            const dragData = new DragManager.DocumentDragData([doc]);
+                            dragData.dropAction = doc.dropAction as dropActionType;
+                            DragManager.StartDocumentDrag([gearSpan], dragData, e.clientX, e.clientY);
+                            return true;
+                        } return false
+                    }, returnFalse, emptyFunction);
                 };
 
                 tab.buttonDisposer = reaction(() => ((view: Opt<DocumentView>) => view ? [view] : [])(DocumentManager.Instance.getDocumentView(doc)),
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 7b7c4d266..d6d9a8cfd 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -36,11 +36,11 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument
                     view.props.Document.layout instanceof Doc ? view.props.Document.layout :
                         view.props.Document.isTemplateForField ? view.props.Document : Doc.GetProto(view.props.Document);
                 if (targetDoc) {
-                    if (view.props.LayoutTemplate || view.props.LayoutTemplateString) {  // this situation typically occurs when you have a link dot 
+                    if (view.props.LayoutTemplate?.() || view.props.LayoutTemplateString) {  // this situation typically occurs when you have a link dot 
                         targetDoc.backgroundColor = Doc.UserDoc().backgroundColor;  // bcz: don't know how to change the color of an inline template...
                     }
-                    else if (StrCast(Doc.Layout(view.props.Document).layout).indexOf("FormattedTextBox") !== -1 && FormattedTextBox.HadSelection) {
-                        Doc.Layout(view.props.Document).color = Doc.UserDoc().backgroundColor;
+                    else if (StrCast(Doc.Layout(view.props.Document).layout).includes("FormattedTextBox") && FormattedTextBox.HadSelection) {
+                        Doc.Layout(view.props.Document)[Doc.LayoutFieldKey(view.props.Document) + "-color"] = Doc.UserDoc().backgroundColor;
                     } else {
                         Doc.Layout(view.props.Document)._backgroundColor = Doc.UserDoc().backgroundColor; // '_backgroundColor' is template specific.  'backgroundColor' would apply to all templates, but has no UI at the moment
                     }
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 1a961ae21..e80951918 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -776,11 +776,11 @@ export default class RichTextMenu extends AntimodeMenu {
                 this.createNodesDropdown(this.activeListType, this.listTypeOptions, "nodes")]}
             </div>
             <div key="button">
-                <div key="collapser">
+                {/* <div key="collapser">
                     <button className="antimodeMenu-button" key="collapse menu" title="Collapse menu" onClick={this.toggleCollapse} style={{ backgroundColor: this.collapsed ? "#121212" : "", width: 25 }}>
                         <FontAwesomeIcon icon="chevron-left" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.3s", transform: `rotate(${this.collapsed ? 180 : 0}deg)` }} />
                     </button>
-                </div>
+                </div> */}
                 <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>
@@ -788,7 +788,7 @@ export default class RichTextMenu extends AntimodeMenu {
         </div>;
 
         return (
-            <div className="richTextMenu" onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+            <div className="richTextMenu" onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} >
                 {this.getElementWithRows([row1, row2], 2, false)}
             </div>
         );
-- 
cgit v1.2.3-70-g09d2


From 7e02ca22f9667d0bdb89b29da3c412ba52976fbf Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 24 Jun 2020 09:52:05 -0400
Subject: fixed placement of settings panel icon & panel.  fixed layout of
 docked text bar.  added blockquote icon

---
 deploy/index.html                                  |  2 +-
 src/client/views/AntimodeMenu.scss                 |  5 +-
 src/client/views/MainView.tsx                      | 54 ++++++++++++----------
 src/client/views/globalCssVariables.scss           |  2 +
 src/client/views/globalCssVariables.scss.d.ts      |  1 +
 .../views/nodes/formattedText/RichTextMenu.tsx     | 39 +++++++++-------
 6 files changed, 60 insertions(+), 43 deletions(-)

(limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')

diff --git a/deploy/index.html b/deploy/index.html
index e441e3610..29a3f15cd 100644
--- a/deploy/index.html
+++ b/deploy/index.html
@@ -10,7 +10,7 @@
 
 <body>
     <!-- <script src="https://hypothes.is/embed.js" async></script> -->
-    <div id="root" style="position:relative"></div>
+    <div id="root" style="position:relative;width:100%;height:100%"></div>
     <script src="/bundle.js"></script>
 </body>
 
diff --git a/src/client/views/AntimodeMenu.scss b/src/client/views/AntimodeMenu.scss
index d4a76ee17..e56574bb7 100644
--- a/src/client/views/AntimodeMenu.scss
+++ b/src/client/views/AntimodeMenu.scss
@@ -1,7 +1,10 @@
+@import "./globalCssVariables";
+
+
 .antimodeMenu-cont {
     position: absolute;
     z-index: 10000;
-    height: 35px;
+    height: $antimodemenu-height;
     background: #323232;
     box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
     border-radius: 0px 6px 6px 6px;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 0686e797e..97953452d 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -5,8 +5,9 @@ import {
     faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt,
     faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
     faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote,
-    faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye
+    faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faQuoteLeft
 } from '@fortawesome/free-solid-svg-icons';
+import { ANTIMODEMENU_HEIGHT } from './globalCssVariables.scss';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
@@ -134,7 +135,7 @@ export class MainView extends React.Component {
             faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt,
             faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
             faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTrashAlt, faAngleRight, faBell,
-            faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye);
+            faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faQuoteLeft);
         this.initEventListeners();
         this.initAuthenticationRouters();
     }
@@ -326,9 +327,7 @@ export class MainView extends React.Component {
         const width = this.flyoutWidth;
         return <Measure offset onResize={this.onResize}>
             {({ measureRef }) =>
-                <div ref={measureRef} className="mainContent-div" onDragEnter={e => {
-                    console.log("ENTERING");
-                }} onDrop={this.onDrop} style={{ width: `calc(100% - ${width}px)` }}>
+                <div ref={measureRef} className="mainContent-div" onDrop={this.onDrop} style={{ width: `calc(100% - ${width}px)` }}>
                     {!mainContainer ? (null) : this.mainDocView}
                 </div>
             }
@@ -459,28 +458,33 @@ export class MainView extends React.Component {
     @computed get mainContent() {
         const sidebar = this.userDoc?.["tabs-panelContainer"];
         return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
-            <div className="mainView-mainContent" style={{ color: this.darkScheme ? "rgb(205,205,205)" : "black" }} >
-                <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger} style={{ width: this.flyoutWidth }}>
-                    <div className="mainView-libraryHandle" onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}
-                        style={{ backgroundColor: this.defaultBackgroundColors(sidebar) }}>
-                        <span title="library View Dragger" style={{
-                            width: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "3vw",
-                            //height: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "100vh",
-                            position: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "absolute" : "fixed",
-                            top: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "" : "0"
-                        }} />
-                    </div>
-                    <div className="mainView-libraryFlyout" style={{
-                        //transformOrigin: this._flyoutTranslate ? "" : "left center",
-                        transition: this._flyoutTranslate ? "" : "width .5s",
-                        //transform: `scale(${this._flyoutTranslate ? 1 : 0.8})`,
-                        boxShadow: this._flyoutTranslate ? "" : "rgb(156, 147, 150) 0.2vw 0.2vw 0.8vw"
-                    }}>
-                        {this.flyout}
-                        {this.expandButton}
+            <div className="mainView-mainContent" style={{
+                color: this.darkScheme ? "rgb(205,205,205)" : "black",
+                height: RichTextMenu.Instance?.Pinned ? `calc(100% - ${ANTIMODEMENU_HEIGHT})` : "100%"
+            }} >
+                <div style={{ display: "contents", flexDirection: "row", position: "relative" }}>
+                    <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger} style={{ width: this.flyoutWidth }}>
+                        <div className="mainView-libraryHandle" onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}
+                            style={{ backgroundColor: this.defaultBackgroundColors(sidebar) }}>
+                            <span title="library View Dragger" style={{
+                                width: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "3vw",
+                                //height: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "100vh",
+                                position: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "absolute" : "fixed",
+                                top: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "" : "0"
+                            }} />
+                        </div>
+                        <div className="mainView-libraryFlyout" style={{
+                            //transformOrigin: this._flyoutTranslate ? "" : "left center",
+                            transition: this._flyoutTranslate ? "" : "width .5s",
+                            //transform: `scale(${this._flyoutTranslate ? 1 : 0.8})`,
+                            boxShadow: this._flyoutTranslate ? "" : "rgb(156, 147, 150) 0.2vw 0.2vw 0.8vw"
+                        }}>
+                            {this.flyout}
+                            {this.expandButton}
+                        </div>
                     </div>
+                    {this.dockingContent}
                 </div>
-                {this.dockingContent}
             </div>);
     }
 
diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss
index 9d3d2e592..4e85fe0ca 100644
--- a/src/client/views/globalCssVariables.scss
+++ b/src/client/views/globalCssVariables.scss
@@ -12,6 +12,7 @@ $lighter-alt-accent: rgb(207, 220, 240);
 $darker-alt-accent: rgb(178, 206, 248);
 $intermediate-color: #9c9396;
 $dark-color: #121721;
+$antimodemenu-height: 35px;
 // fonts
 $sans-serif: "Noto Sans",
 sans-serif;
@@ -40,4 +41,5 @@ $MAX_ROW_HEIGHT: 44px;
     MINIMIZED_ICON_SIZE: $MINIMIZED_ICON_SIZE;
     MAX_ROW_HEIGHT: $MAX_ROW_HEIGHT;
     SEARCH_THUMBNAIL_SIZE: $search-thumnail-size;
+    ANTIMODEMENU_HEIGHT: $antimodemenu-height;
 }
\ No newline at end of file
diff --git a/src/client/views/globalCssVariables.scss.d.ts b/src/client/views/globalCssVariables.scss.d.ts
index d95cec9d8..a7ca4b300 100644
--- a/src/client/views/globalCssVariables.scss.d.ts
+++ b/src/client/views/globalCssVariables.scss.d.ts
@@ -5,6 +5,7 @@ interface IGlobalScss {
     MINIMIZED_ICON_SIZE: string;
     MAX_ROW_HEIGHT: string;
     SEARCH_THUMBNAIL_SIZE: string;
+    ANTIMODEMENU_HEIGHT: string;
 }
 declare const globalCssVariables: IGlobalScss;
 
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index e80951918..68101fbde 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -161,11 +161,10 @@ export default class RichTextMenu extends AntimodeMenu {
             return;
         }
         this.view = view;
-        const state = view.state;
         props && (this.editorProps = props);
 
         // Don't do anything if the document/selection didn't change
-        if (lastState && lastState.doc.eq(state.doc) && lastState.selection.eq(state.selection)) return;
+        if (lastState?.doc.eq(view.state.doc) && lastState.selection.eq(view.state.selection)) return;
 
         // update active marks
         const activeMarks = this.getActiveMarksOnSelection();
@@ -173,18 +172,18 @@ export default class RichTextMenu extends AntimodeMenu {
 
         // update active font family and size
         const active = this.getActiveFontStylesOnSelection();
-        const activeFamilies = active && active.get("families");
-        const activeSizes = active && active.get("sizes");
+        const activeFamilies = active?.get("families");
+        const activeSizes = active?.get("sizes");
 
-        this.activeFontFamily = !activeFamilies || activeFamilies.length === 0 ? "Arial" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various";
-        this.activeFontSize = !activeSizes || activeSizes.length === 0 ? "13pt" : activeSizes.length === 1 ? String(activeSizes[0]) + "pt" : "various";
+        this.activeFontFamily = !activeFamilies?.length ? "Arial" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various";
+        this.activeFontSize = !activeSizes?.length ? "13pt" : activeSizes.length === 1 ? String(activeSizes[0]) : "various";
 
         // update link in current selection
         const targetTitle = await this.getTextLinkTargetTitle();
         this.setCurrentLink(targetTitle);
     }
 
-    setMark = (mark: Mark, state: EditorState<any>, dispatch: any) => {
+    setMark = (mark: Mark, state: EditorState<any>, dispatch: any, dontToggle: boolean = false) => {
         if (mark) {
             const node = (state.selection as NodeSelection).node;
             if (node?.type === schema.nodes.ordered_list) {
@@ -194,14 +193,15 @@ export default class RichTextMenu extends AntimodeMenu {
                 if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, fontColor: mark.attrs.color };
                 const tr = updateBullets(state.tr.setNodeMarkup(state.selection.from, node.type, attrs), state.schema);
                 dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(state.selection.from))));
-            } else {
+            } else if (dontToggle) {
                 toggleMark(mark.type, mark.attrs)(state, (tx: any) => {
                     const { from, $from, to, empty } = tx.selection;
-                    // if (!tx.doc.rangeHasMark(from, to, mark.type)) {
-                    //     toggleMark(mark.type, mark.attrs)({ tr: tx, doc: tx.doc, selection: tx.selection, storedMarks: tx.storedMarks }, dispatch);
-                    // } else
-                    dispatch(tx);
+                    if (!tx.doc.rangeHasMark(from, to, mark.type)) { // hack -- should have just set the mark in the first place
+                        toggleMark(mark.type, mark.attrs)({ tr: tx, doc: tx.doc, selection: tx.selection, storedMarks: tx.storedMarks }, dispatch);
+                    } else dispatch(tx);
                 });
+            } else {
+                toggleMark(mark.type, mark.attrs)(state, (tx: any) => dispatch(tx));
             }
         }
     }
@@ -368,11 +368,11 @@ export default class RichTextMenu extends AntimodeMenu {
     }
 
     changeFontSize = (mark: Mark, view: EditorView) => {
-        this.setMark(view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize }), view.state, view.dispatch);
+        this.setMark(view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize }), view.state, view.dispatch, true);
     }
 
     changeFontFamily = (mark: Mark, view: EditorView) => {
-        this.setMark(view.state.schema.marks.pFontFamily.create({ family: mark.attrs.family }), view.state, view.dispatch);
+        this.setMark(view.state.schema.marks.pFontFamily.create({ family: mark.attrs.family }), view.state, view.dispatch, true);
     }
 
     // TODO: remove doesn't work
@@ -406,7 +406,13 @@ export default class RichTextMenu extends AntimodeMenu {
         tr.addMark(state.selection.from, state.selection.to, mark);
         const content = tr.selection.content();
         const newNode = state.schema.nodes.summary.create({ visibility: false, text: content, textslice: content.toJSON() });
-        dispatch && dispatch(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
+        dispatch?.(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
+        return true;
+    }
+
+    insertBlockquote(state: EditorState<any>, dispatch: any) {
+        if (state.selection.empty) return false;
+        setBlockType(state.schema.nodes.blockquote)(state, (tx: any) => dispatch(tx));
         return true;
     }
 
@@ -539,7 +545,7 @@ export default class RichTextMenu extends AntimodeMenu {
             dispatch(state.tr.addStoredMark(colorMark));
             return false;
         }
-        this.setMark(colorMark, state, dispatch);
+        this.setMark(colorMark, state, dispatch, true);
     }
 
     @action toggleHighlightDropdown() { this.showHighlightDropdown = !this.showHighlightDropdown; }
@@ -766,6 +772,7 @@ export default class RichTextMenu extends AntimodeMenu {
             this.createLinkButton(),
             this.createBrushButton(),
             this.createButton("indent", "Summarize", undefined, this.insertSummarizer),
+            this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote),
         ]}</div>;
 
         const row2 = <div className="antimodeMenu-row row-2" key="antimodemenu row2">
-- 
cgit v1.2.3-70-g09d2


From ae6315813d718f8e4913ed98f5aec33464eb8164 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 24 Jun 2020 11:45:22 -0400
Subject: fixed warnings.  fixed blockquote menu function.

---
 src/client/util/DragManager.ts                     |  2 +-
 .../views/collections/CollectionDockingView.tsx    |  3 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  5 +--
 .../views/nodes/formattedText/ParagraphNodeSpec.ts |  2 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     | 50 +++++++++++++---------
 src/client/views/nodes/formattedText/nodes_rts.ts  | 20 ++++++++-
 .../nodes/formattedText/prosemirrorPatches.js      |  4 +-
 7 files changed, 56 insertions(+), 30 deletions(-)

(limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 533bc8485..91bc51101 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -218,7 +218,7 @@ export namespace DragManager {
                 docDragData.dropAction !== "same" && docDragData.droppedDocuments.forEach((drop: Doc, i: number) => {
                     const dragProps = Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []);
                     const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps));
-                    remProps.map(prop => drop[prop] = undefined)
+                    remProps.map(prop => drop[prop] = undefined);
                 });
                 batch.end();
             }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index ee987abdb..79c577b6d 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -527,7 +527,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                             dragData.dropAction = doc.dropAction as dropActionType;
                             DragManager.StartDocumentDrag([gearSpan], dragData, e.clientX, e.clientY);
                             return true;
-                        } return false
+                        }
+                        return false;
                     }, returnFalse, emptyFunction);
                 };
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index e01144f82..cdf231985 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -933,7 +933,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 clipboardTextSerializer: this.clipboardTextSerializer,
                 handlePaste: this.handlePaste,
             });
-            // applyDevTools.applyDevTools(this._editorView);
+            //applyDevTools.applyDevTools(this._editorView);
             const startupText = !rtfField && this._editorView && Field.toString(this.dataDoc[fieldKey] as Field);
             if (startupText) {
                 const { state: { tr }, dispatch } = this._editorView;
@@ -1043,11 +1043,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         if (bounds && this.layoutDoc._chromeStatus !== "disabled") {
             const x = Math.min(Math.max(bounds.left, 0), window.innerWidth - RichTextMenu.Instance.width);
             let y = Math.min(Math.max(0, bounds.top - RichTextMenu.Instance.height - 50), window.innerHeight - RichTextMenu.Instance.height);
-            console.log("y = " + y + " hgt = " + RichTextMenu.Instance.height + " cords = " + coords.top);
             if (coords && coords.left > x && coords.left < x + RichTextMenu.Instance.width && coords.top > y && coords.top < y + RichTextMenu.Instance.height + 50) {
                 y = Math.min(bounds.bottom, window.innerHeight - RichTextMenu.Instance.height);
             }
-            RichTextMenu.Instance.jumpTo(x, y);
+            setTimeout(() => window.document.activeElement === this.ProseRef?.children[0] && RichTextMenu.Instance.jumpTo(x, y), 250);
         }
     }
     onPointerWheel = (e: React.WheelEvent): void => {
diff --git a/src/client/views/nodes/formattedText/ParagraphNodeSpec.ts b/src/client/views/nodes/formattedText/ParagraphNodeSpec.ts
index d80e64634..30da91710 100644
--- a/src/client/views/nodes/formattedText/ParagraphNodeSpec.ts
+++ b/src/client/views/nodes/formattedText/ParagraphNodeSpec.ts
@@ -28,7 +28,7 @@ const ALIGN_PATTERN = /(left|right|center|justify)/;
 // https://github.com/ProseMirror/prosemirror-schema-basic/blob/master/src/schema-basic.js
 // :: NodeSpec A plain paragraph textblock. Represented in the DOM
 // as a `<p>` element.
-const ParagraphNodeSpec: NodeSpec = {
+export const ParagraphNodeSpec: NodeSpec = {
     attrs: {
         align: { default: null },
         color: { default: null },
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 68101fbde..5e32b3ef8 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -1,30 +1,33 @@
 import React = require("react");
-import AntimodeMenu from "../../AntimodeMenu";
-import { observable, action, } from "mobx";
+import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
+import { faBold, faCaretDown, faChevronLeft, faEyeDropper, faHighlighter, faIndent, faItalic, faLink, faPaintRoller, faPalette, faStrikethrough, faSubscript, faSuperscript, faUnderline } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, observable } from "mobx";
 import { observer } from "mobx-react";
-import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos, Schema } from "prosemirror-model";
-import { schema } from "./schema_rts";
-import { EditorView } from "prosemirror-view";
+import { lift, wrapIn } from "prosemirror-commands";
+import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos } from "prosemirror-model";
+import { wrapInList } from "prosemirror-schema-list";
 import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
-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, faHighlighter, faLink, faPaintRoller, faSleigh } from "@fortawesome/free-solid-svg-icons";
-import { updateBullets } from "./ProsemirrorExampleTransfer";
-import { FieldViewProps } from "../FieldView";
+import { EditorView } from "prosemirror-view";
+import { Doc } from "../../../../fields/Doc";
+import { DarkPastelSchemaPalette, PastelSchemaPalette } from '../../../../fields/SchemaHeaderField';
 import { Cast, StrCast } from "../../../../fields/Types";
-import { FormattedTextBoxProps, FormattedTextBox } from "./FormattedTextBox";
 import { unimplementedFunction, Utils } from "../../../../Utils";
-import { wrapInList } from "prosemirror-schema-list";
-import { PastelSchemaPalette, DarkPastelSchemaPalette } from '../../../../fields/SchemaHeaderField';
-import "./RichTextMenu.scss";
 import { DocServer } from "../../../DocServer";
-import { Doc } from "../../../../fields/Doc";
-import { SelectionManager } from "../../../util/SelectionManager";
 import { LinkManager } from "../../../util/LinkManager";
-const { toggleMark, setBlockType } = require("prosemirror-commands");
+import { SelectionManager } from "../../../util/SelectionManager";
+import AntimodeMenu from "../../AntimodeMenu";
+import { FieldViewProps } from "../FieldView";
+import { FormattedTextBox, FormattedTextBoxProps } from "./FormattedTextBox";
+import { updateBullets } from "./ProsemirrorExampleTransfer";
+import "./RichTextMenu.scss";
+import { schema } from "./schema_rts";
+import { TraceMobx } from "../../../../fields/util";
+const { toggleMark } = require("prosemirror-commands");
 
 library.add(faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, faCaretDown, faPalette, faHighlighter, faLink, faPaintRoller);
 
+
 @observer
 export default class RichTextMenu extends AntimodeMenu {
     static Instance: RichTextMenu;
@@ -69,6 +72,7 @@ export default class RichTextMenu extends AntimodeMenu {
         super(props);
         RichTextMenu.Instance = this;
         this._canFade = false;
+        this.Pinned = true;
 
         this.fontSizeOptions = [
             { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize },
@@ -201,7 +205,7 @@ export default class RichTextMenu extends AntimodeMenu {
                     } else dispatch(tx);
                 });
             } else {
-                toggleMark(mark.type, mark.attrs)(state, (tx: any) => dispatch(tx));
+                toggleMark(mark.type, mark.attrs)(state, dispatch);
             }
         }
     }
@@ -411,8 +415,12 @@ export default class RichTextMenu extends AntimodeMenu {
     }
 
     insertBlockquote(state: EditorState<any>, dispatch: any) {
-        if (state.selection.empty) return false;
-        setBlockType(state.schema.nodes.blockquote)(state, (tx: any) => dispatch(tx));
+        const path = (state.selection.$from as any).path;
+        if (path.length > 6 && path[path.length - 6].type === schema.nodes.blockquote) {
+            lift(state, dispatch);
+        } else {
+            wrapIn(schema.nodes.blockquote)(state, dispatch);
+        }
         return true;
     }
 
@@ -758,7 +766,7 @@ export default class RichTextMenu extends AntimodeMenu {
     }
 
     render() {
-
+        TraceMobx();
         const row1 = <div className="antimodeMenu-row" key="row1" style={{ display: this.collapsed ? "none" : undefined }}>{[
             !this.collapsed ? this.getDragger() : (null),
             this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)),
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 1a292d9af..0a867912f 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -1,7 +1,7 @@
 import React = require("react");
 import { DOMOutputSpecArray, Fragment, MarkSpec, Node, NodeSpec, Schema, Slice } from "prosemirror-model";
 import { bulletList, listItem, orderedList } from 'prosemirror-schema-list';
-import ParagraphNodeSpec from "./ParagraphNodeSpec";
+import { ParagraphNodeSpec, toParagraphDOM, getParagraphNodeAttrs } from "./ParagraphNodeSpec";
 
 const blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
     preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
@@ -32,13 +32,29 @@ export const nodes: { [index: string]: NodeSpec } = {
 
     // :: NodeSpec A blockquote (`<blockquote>`) wrapping one or more blocks.
     blockquote: {
-        content: "block+",
+        content: "block*",
         group: "block",
         defining: true,
         parseDOM: [{ tag: "blockquote" }],
         toDOM() { return blockquoteDOM; }
     },
 
+
+    // blockquote: {
+    //     ...ParagraphNodeSpec,
+    //     defining: true,
+    //     parseDOM: [{
+    //         tag: "blockquote", getAttrs(dom: any) {
+    //             return getParagraphNodeAttrs(dom);
+    //         }
+    //     }],
+    //     toDOM(node: any) {
+    //         const dom = toParagraphDOM(node);
+    //         (dom as any)[0] = 'blockquote';
+    //         return dom;
+    //     },
+    // },
+
     // :: NodeSpec A horizontal rule (`<hr>`).
     horizontal_rule: {
         group: "block",
diff --git a/src/client/views/nodes/formattedText/prosemirrorPatches.js b/src/client/views/nodes/formattedText/prosemirrorPatches.js
index 269423482..763961958 100644
--- a/src/client/views/nodes/formattedText/prosemirrorPatches.js
+++ b/src/client/views/nodes/formattedText/prosemirrorPatches.js
@@ -136,4 +136,6 @@ function wrappingInputRule(regexp, nodeType, getAttrs, joinPredicate, customWith
             (!joinPredicate || joinPredicate(match, before))) { tr.join(start - 1); }
         return tr
     })
-}
\ No newline at end of file
+}
+
+
-- 
cgit v1.2.3-70-g09d2


From ab55a59f47efdd8f134a619ca0fa59203efe2fa9 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 24 Jun 2020 13:43:23 -0400
Subject: fixed summary icon

---
 src/client/views/MainView.tsx                         | 4 ++--
 src/client/views/nodes/formattedText/RichTextMenu.tsx | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 97953452d..415f8b364 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -5,7 +5,7 @@ import {
     faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt,
     faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
     faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote,
-    faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faQuoteLeft
+    faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faQuoteLeft, faSortAmountDown
 } from '@fortawesome/free-solid-svg-icons';
 import { ANTIMODEMENU_HEIGHT } from './globalCssVariables.scss';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -135,7 +135,7 @@ export class MainView extends React.Component {
             faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt,
             faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
             faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTrashAlt, faAngleRight, faBell,
-            faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faQuoteLeft);
+            faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faQuoteLeft, faSortAmountDown);
         this.initEventListeners();
         this.initAuthenticationRouters();
     }
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 5e32b3ef8..3a4363587 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -779,7 +779,7 @@ export default class RichTextMenu extends AntimodeMenu {
             this.createHighlighterButton(),
             this.createLinkButton(),
             this.createBrushButton(),
-            this.createButton("indent", "Summarize", undefined, this.insertSummarizer),
+            this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer),
             this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote),
         ]}</div>;
 
-- 
cgit v1.2.3-70-g09d2


From 8e9f77d93ec4393c52a215e75bcd9a362173e06a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 24 Jun 2020 16:24:37 -0400
Subject: made persistent link Icon in bottom left of document

---
 src/client/views/DocumentButtonBar.tsx             | 18 +++---
 src/client/views/DocumentDecorations.tsx           |  2 +-
 src/client/views/MainView.tsx                      |  3 +
 src/client/views/globalCssVariables.scss           |  1 +
 src/client/views/linking/LinkMenu.scss             |  4 ++
 src/client/views/linking/LinkMenu.tsx              | 37 ++++++-----
 src/client/views/nodes/DocumentLinksButton.scss    | 39 +++++++++++
 src/client/views/nodes/DocumentLinksButton.tsx     | 75 ++++++++++++++++++++++
 src/client/views/nodes/DocumentView.tsx            | 10 +--
 .../views/nodes/formattedText/RichTextMenu.tsx     |  7 +-
 10 files changed, 160 insertions(+), 36 deletions(-)
 create mode 100644 src/client/views/nodes/DocumentLinksButton.scss
 create mode 100644 src/client/views/nodes/DocumentLinksButton.tsx

(limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')

diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 62a95116f..208b4d57a 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -239,14 +239,14 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
     get linkButton() {
         const view0 = this.view0;
         const linkCount = view0 && DocListCast(view0.props.Document.links).length;
-        return !view0 ? (null) : <div title="Drag(create link) Tap(view links)" className="documentButtonBar-linkFlyout" ref={this._linkButton}>
-            <Flyout anchorPoint={anchorPoints.LEFT_TOP}
-                content={<LinkMenu docView={view0} addDocTab={view0.props.addDocTab} changeFlyout={emptyFunction} />}>
-                <div className={"documentButtonBar-linkButton-" + (linkCount ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >
-                    {linkCount ? linkCount : <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" />}
+        return !view0 || linkCount ? (null) :
+            <div className="documentButtonBar-button">
+                <div title="Drag(create link) Tap(view links)" className="documentButtonBar-linkFlyout" ref={this._linkButton}>
+                    <div className={"documentButtonBar-linkButton-" + (linkCount ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >
+                        {linkCount ? linkCount : <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" />}
+                    </div>
                 </div>
-            </Flyout>
-        </div>;
+            </div>;
     }
 
     @computed
@@ -317,9 +317,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         const considerPull = isText && this.considerGoogleDocsPull;
         const considerPush = isText && this.considerGoogleDocsPush;
         return <div className="documentButtonBar">
-            <div className="documentButtonBar-button">
-                {this.linkButton}
-            </div>
+            {this.linkButton}
             <div className="documentButtonBar-button">
                 {this.templateButton}
             </div>
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index beb6155ca..d7b0ab7a9 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -552,7 +552,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                     onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}></div>
 
             </div >
-            <div className="link-button-container" style={{ left: bounds.x - this._resizeBorderWidth / 2, top: bounds.b + this._resizeBorderWidth / 2 }}>
+            <div className="link-button-container" style={{ left: bounds.x - this._resizeBorderWidth / 2 + 10, top: bounds.b + this._resizeBorderWidth / 2 }}>
                 <DocumentButtonBar views={SelectionManager.SelectedDocuments} />
             </div>
         </div >
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 415f8b364..374fd7421 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -55,6 +55,8 @@ import { TimelineMenu } from './animationtimeline/TimelineMenu';
 import { SnappingManager } from '../util/SnappingManager';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { DocumentManager } from '../util/DocumentManager';
+import { DocumentLinksButton } from './nodes/DocumentLinksButton';
+import { LinkMenu } from './linking/LinkMenu';
 
 @observer
 export class MainView extends React.Component {
@@ -578,6 +580,7 @@ export class MainView extends React.Component {
                 {this.mainContent}
             </GestureOverlay>
             <PreviewCursor />
+            {DocumentLinksButton.EditLink ? <LinkMenu location={DocumentLinksButton.EditLinkLoc} docView={DocumentLinksButton.EditLink} addDocTab={DocumentLinksButton.EditLink.props.addDocTab} changeFlyout={emptyFunction} /> : (null)}
             <ContextMenu />
             <RadialMenu />
             <PDFMenu />
diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss
index 4e85fe0ca..3e54d001b 100644
--- a/src/client/views/globalCssVariables.scss
+++ b/src/client/views/globalCssVariables.scss
@@ -12,6 +12,7 @@ $lighter-alt-accent: rgb(207, 220, 240);
 $darker-alt-accent: rgb(178, 206, 248);
 $intermediate-color: #9c9396;
 $dark-color: #121721;
+$link-color: lightBlue;
 $antimodemenu-height: 35px;
 // fonts
 $sans-serif: "Noto Sans",
diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss
index 7dee22f66..c372e7098 100644
--- a/src/client/views/linking/LinkMenu.scss
+++ b/src/client/views/linking/LinkMenu.scss
@@ -8,6 +8,10 @@
 .linkMenu-list {
     max-height: 200px;
     overflow-y: scroll;
+    position: absolute;
+    z-index: 10;
+    background: $link-color;
+    min-width: 150px
 }
 
 .linkMenu-group {
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 56f40ad69..0fcc0f0b9 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -4,11 +4,12 @@ import { DocumentView } from "../nodes/DocumentView";
 import { LinkEditor } from "./LinkEditor";
 import './LinkMenu.scss';
 import React = require("react");
-import { Doc } from "../../../fields/Doc";
+import { Doc, Opt } from "../../../fields/Doc";
 import { LinkManager } from "../../util/LinkManager";
 import { LinkMenuGroup } from "./LinkMenuGroup";
 import { faTrash } from '@fortawesome/free-solid-svg-icons';
 import { library } from "@fortawesome/fontawesome-svg-core";
+import { DocumentLinksButton } from "../nodes/DocumentLinksButton";
 
 library.add(faTrash);
 
@@ -16,16 +17,29 @@ interface Props {
     docView: DocumentView;
     changeFlyout: () => void;
     addDocTab: (document: Doc, where: string) => boolean;
+    location: number[];
 }
 
 @observer
 export class LinkMenu extends React.Component<Props> {
 
     @observable private _editingLink?: Doc;
+    @observable private _linkMenuRef: Opt<HTMLDivElement | null>;
 
+    @action
+    onClick = (e: PointerEvent) => {
+        if (!Array.from(this._linkMenuRef?.getElementsByTagName((e.target as HTMLElement).tagName) || []).includes(e.target as any)) {
+            DocumentLinksButton.EditLink = undefined;
+        }
+    }
     @action
     componentDidMount() {
         this._editingLink = undefined;
+        document.addEventListener("pointerdown", this.onClick);
+    }
+
+    componentWillUnmount() {
+        document.removeEventListener("pointerdown", this.onClick);
     }
 
     clearAllLinks = () => {
@@ -56,20 +70,11 @@ export class LinkMenu extends React.Component<Props> {
     render() {
         const sourceDoc = this.props.docView.props.Document;
         const groups: Map<string, Doc[]> = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc);
-        if (this._editingLink === undefined) {
-            return (
-                <div className="linkMenu">
-                    {/* <button className="linkEditor-button linkEditor-clearButton" onClick={() => this.clearAllLinks()} title="Clear all links"><FontAwesomeIcon icon="trash" size="sm" /></button> */}
-                    {/* <input id="linkMenu-searchBar" type="text" placeholder="Search..."></input> */}
-                    <div className="linkMenu-list">
-                        {this.renderAllGroups(groups)}
-                    </div>
-                </div>
-            );
-        } else {
-            return (
-                <LinkEditor sourceDoc={this.props.docView.props.Document} linkDoc={this._editingLink} showLinks={action(() => this._editingLink = undefined)}></LinkEditor>
-            );
-        }
+        return <div className="linkMenu-list" ref={(r) => this._linkMenuRef = r} style={{ left: this.props.location[0], top: this.props.location[1] }}>
+            {!this._editingLink ?
+                this.renderAllGroups(groups) :
+                <LinkEditor sourceDoc={this.props.docView.props.Document} linkDoc={this._editingLink} showLinks={action(() => this._editingLink = undefined)} />
+            }
+        </div>;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss
new file mode 100644
index 000000000..b9d5d437b
--- /dev/null
+++ b/src/client/views/nodes/DocumentLinksButton.scss
@@ -0,0 +1,39 @@
+@import "../globalCssVariables.scss";
+
+
+.documentLinksButton-button-empty:hover {
+    background: $main-accent;
+    transform: scale(1.05);
+    cursor: pointer;
+}
+
+.documentLinksButton-button-nonempty:hover {
+    background: $main-accent;
+    transform: scale(1.05);
+    cursor: pointer;
+}
+
+.documentLinksButton-button-empty,
+.documentLinksButton-button-nonempty {
+    height: 20px;
+    width: 20px;
+    border-radius: 50%;
+    opacity: 0.9;
+    pointer-events: auto;
+    background-color: $link-color;
+    color: black;
+    text-transform: uppercase;
+    letter-spacing: 2px;
+    font-size: 75%;
+    transition: transform 0.2s;
+    text-align: center;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    &:hover {
+        background: $main-accent;
+        transform: scale(1.05);
+        cursor: pointer;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
new file mode 100644
index 000000000..4f94b06cb
--- /dev/null
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -0,0 +1,75 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { computed, action, runInAction, observable } from "mobx";
+import { observer } from "mobx-react";
+import './DocumentLinksButton.scss';
+import React = require("react");
+import { emptyFunction, setupMoveUpEvents } from "../../../Utils";
+import { DocListCast, Doc } from "../../../fields/Doc";
+import { DocumentView } from "./DocumentView";
+import { LinkMenu } from "../linking/LinkMenu";
+import { UndoManager } from "../../util/UndoManager";
+import { DragManager } from "../../util/DragManager";
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
+
+interface DocumentLinksButtonProps {
+    View: DocumentView;
+}
+@observer
+export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> {
+    private _linkButton = React.createRef<HTMLDivElement>();
+
+    @action
+    onLinkButtonMoved = (e: PointerEvent) => {
+        if (this._linkButton.current !== null) {
+            const linkDrag = UndoManager.StartBatch("Drag Link");
+            this.props.View && DragManager.StartLinkDrag(this._linkButton.current, this.props.View.props.Document, e.pageX, e.pageY, {
+                dragComplete: dropEv => {
+                    const linkDoc = dropEv.linkDragData?.linkDocument as Doc; // equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop
+                    if (this.props.View && linkDoc) {
+                        !linkDoc.linkRelationship && (Doc.GetProto(linkDoc).linkRelationship = "hyperlink");
+
+                        // we want to allow specific views to handle the link creation in their own way (e.g., rich text makes text hyperlinks)
+                        // the dragged view can regiser a linkDropCallback to be notified that the link was made and to update their data structures
+                        // however, the dropped document isn't so accessible.  What we do is set the newly created link document on the documentView
+                        // The documentView passes a function prop returning this link doc to its descendants who can react to changes to it.
+                        dropEv.linkDragData?.linkDropCallback?.(dropEv.linkDragData);
+                        runInAction(() => this.props.View._link = linkDoc);
+                        setTimeout(action(() => this.props.View._link = undefined), 0);
+                    }
+                    linkDrag?.end();
+                },
+                hideSource: false
+            });
+            return true;
+        }
+        return false;
+    }
+
+    onLinkButtonDown = (e: React.PointerEvent): void => {
+        setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e) => {
+            DocumentLinksButton.EditLink = this.props.View;
+            DocumentLinksButton.EditLinkLoc = [e.clientX, e.clientY];
+        }));
+    }
+
+    @observable
+    public static EditLink: DocumentView | undefined;
+    public static EditLinkLoc: number[] = [0, 0];
+
+    @computed
+    get linkButton() {
+        const view0 = this.props.View;
+        const linkCount = view0 && DocListCast(view0.props.Document.links).length;
+        return !view0 || !linkCount ? (null) :
+            <div title="Drag(create link) Tap(view links)" style={{ position: "absolute", left: -15, bottom: -15 }} ref={this._linkButton}>
+                <div className={"documentLinksButton-button-" + (linkCount ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >
+                    {linkCount ? linkCount : <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" />}
+                </div>
+            </div>;
+    }
+    render() {
+        return this.linkButton;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7e20b40a3..d9a211e7a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -41,6 +41,7 @@ import "./DocumentView.scss";
 import { LinkAnchorBox } from './LinkAnchorBox';
 import { RadialMenu } from './RadialMenu';
 import React = require("react");
+import { DocumentLinksButton } from './DocumentLinksButton';
 
 library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight,
     fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale,
@@ -735,10 +736,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
         const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
         templateDoc && optionItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" });
-        if (!options) {
-            options = { description: "Options...", subitems: optionItems, icon: "compass" };
-            cm.addItem(options);
-        }
+        optionItems.push({ description: "Toggle Show Each Link Dot", event: () => this.layoutDoc.showLinks = !this.layoutDoc.showLinks, icon: "eye" });
+        !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
 
         const existingOnClick = cm.findByDescription("OnClick...");
         const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
@@ -1037,7 +1036,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 select={this.select}
                 onClick={this.onClickHandler}
                 layoutKey={this.finalLayoutKey} />
-            {this.anchors}
+            {this.layoutDoc.showLinks ? this.anchors : (null)}
+            {this.props.forcedBackgroundColor?.(this.Document) === "transparent" ? (null) : <DocumentLinksButton View={this} />}
         </div>
         );
     }
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 3a4363587..839943aac 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -11,7 +11,7 @@ import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
 import { Doc } from "../../../../fields/Doc";
 import { DarkPastelSchemaPalette, PastelSchemaPalette } from '../../../../fields/SchemaHeaderField';
-import { Cast, StrCast } from "../../../../fields/Types";
+import { Cast, StrCast, BoolCast } from "../../../../fields/Types";
 import { unimplementedFunction, Utils } from "../../../../Utils";
 import { DocServer } from "../../../DocServer";
 import { LinkManager } from "../../../util/LinkManager";
@@ -72,7 +72,7 @@ export default class RichTextMenu extends AntimodeMenu {
         super(props);
         RichTextMenu.Instance = this;
         this._canFade = false;
-        this.Pinned = true;
+        this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]);
 
         this.fontSizeOptions = [
             { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize },
@@ -147,7 +147,6 @@ export default class RichTextMenu extends AntimodeMenu {
         this.updateFromDash(view, lastState, this.editorProps);
     }
 
-
     public MakeLinkToSelection = (linkDocId: string, title: string, location: string, targetDocId: string): string => {
         if (this.view) {
             const link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + linkDocId), title: title, location: location, linkId: linkDocId, targetId: targetDocId });
@@ -750,7 +749,7 @@ export default class RichTextMenu extends AntimodeMenu {
 
     @action
     toggleMenuPin = (e: React.MouseEvent) => {
-        this.Pinned = !this.Pinned;
+        Doc.UserDoc()["menuRichText-pinned"] = this.Pinned = !this.Pinned;
         if (!this.Pinned) {
             this.fadeOut(true);
         }
-- 
cgit v1.2.3-70-g09d2

