From d220b23867050fb6565fe639c3be28080f589aa2 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 11 Nov 2019 18:39:23 -0500
Subject: made font size and font style text marks be parametrized to simplify
 api.

---
 src/client/util/TooltipTextMenu.tsx | 277 +++++++++++++++++++++---------------
 1 file changed, 165 insertions(+), 112 deletions(-)

(limited to 'src/client/util/TooltipTextMenu.tsx')

diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 5cbf8b761..9ce7acec8 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -29,11 +29,9 @@ export class TooltipTextMenu {
     public tooltip: HTMLElement;
     private view: EditorView;
     private editorProps: FieldViewProps & FormattedTextBoxProps | undefined;
-    private fontStyles: MarkType[];
-    private fontSizes: MarkType[];
     private listTypes: (NodeType | any)[];
-    private fontSizeToNum: Map<MarkType, number>;
-    private fontStylesToName: Map<MarkType, string>;
+    private fontSizes: Mark[] = [];
+    private fontStyles: Mark[] = [];
     private listTypeToIcon: Map<NodeType | any, string>;
     //private link: HTMLAnchorElement;
     private wrapper: HTMLDivElement;
@@ -135,46 +133,32 @@ export class TooltipTextMenu {
 
 
         //list of font styles
-        this.fontStylesToName = new Map();
-        this.fontStylesToName.set(schema.marks.timesNewRoman, "Times New Roman");
-        this.fontStylesToName.set(schema.marks.arial, "Arial");
-        this.fontStylesToName.set(schema.marks.georgia, "Georgia");
-        this.fontStylesToName.set(schema.marks.comicSans, "Comic Sans MS");
-        this.fontStylesToName.set(schema.marks.tahoma, "Tahoma");
-        this.fontStylesToName.set(schema.marks.impact, "Impact");
-        this.fontStylesToName.set(schema.marks.crimson, "Crimson Text");
-        this.fontStyles = Array.from(this.fontStylesToName.keys());
+        this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Times New Roman" }));
+        this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Arial" }));
+        this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Georgia" }));
+        this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Comic Sans MS" }));
+        this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Tahoma" }));
+        this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Impact" }));
+        this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Crimson Text" }));
 
         //font sizes
-        this.fontSizeToNum = new Map();
-        this.fontSizeToNum.set(schema.marks.p10, 10);
-        this.fontSizeToNum.set(schema.marks.p12, 12);
-        this.fontSizeToNum.set(schema.marks.p14, 14);
-        this.fontSizeToNum.set(schema.marks.p16, 16);
-        this.fontSizeToNum.set(schema.marks.p18, 18);
-        this.fontSizeToNum.set(schema.marks.p20, 20);
-        this.fontSizeToNum.set(schema.marks.p24, 24);
-        this.fontSizeToNum.set(schema.marks.p32, 32);
-        this.fontSizeToNum.set(schema.marks.p48, 48);
-        this.fontSizeToNum.set(schema.marks.p72, 72);
-        this.fontSizeToNum.set(schema.marks.pFontSize, 10);
-        // this.fontSizeToNum.set(schema.marks.pFontSize, 12);
-        // this.fontSizeToNum.set(schema.marks.pFontSize, 14);
-        // this.fontSizeToNum.set(schema.marks.pFontSize, 16);
-        // this.fontSizeToNum.set(schema.marks.pFontSize, 18);
-        // this.fontSizeToNum.set(schema.marks.pFontSize, 20);
-        // this.fontSizeToNum.set(schema.marks.pFontSize, 24);
-        // this.fontSizeToNum.set(schema.marks.pFontSize, 32);
-        // this.fontSizeToNum.set(schema.marks.pFontSize, 48);
-        // this.fontSizeToNum.set(schema.marks.pFontSize, 72);
-        this.fontSizes = Array.from(this.fontSizeToNum.keys());
+        this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 10 }));
+        this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 12 }));
+        this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 14 }));
+        this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 16 }));
+        this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 18 }));
+        this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 20 }));
+        this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 24 }));
+        this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 32 }));
+        this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 48 }));
+        this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 72 }));
 
         //list types
         this.listTypeToIcon = new Map();
         //this.listTypeToIcon.set(schema.nodes.bullet_list, ":");
         this.listTypeToIcon.set(schema.nodes.ordered_list.create({ mapStyle: "bullet" }), ":");
-        this.listTypeToIcon.set(schema.nodes.ordered_list.create({ mapStyle: "decimal" }), "1.1");
-        this.listTypeToIcon.set(schema.nodes.ordered_list.create({ mapStyle: "multi" }), "1.A");
+        this.listTypeToIcon.set(schema.nodes.ordered_list.create({ mapStyle: "decimal" }), "1.1)");
+        this.listTypeToIcon.set(schema.nodes.ordered_list.create({ mapStyle: "multi" }), "1.A)");
         // this.listTypeToIcon.set(schema.nodes.bullet_list, "⬜");
         this.listTypes = Array.from(this.listTypeToIcon.keys());
 
@@ -195,16 +179,13 @@ export class TooltipTextMenu {
 
     //label of dropdown will change to given label
     updateFontSizeDropdown(label: string) {
-        //filtering function - might be unecessary
-        let cut = (arr: MenuItem[]) => arr.filter(x => x);
-
         //font SIZES
         let fontSizeBtns: MenuItem[] = [];
-        this.fontSizeToNum.forEach((number, mark) => {
-            fontSizeBtns.push(this.dropdownMarkBtn(String(number), "color: black; width: 50px;", mark, this.view, this.changeToMarkInGroup, this.fontSizes));
+        this.fontSizes.forEach(mark => {
+            fontSizeBtns.push(this.dropdownFontSizeBtn(String(mark.attrs.fontSize), "color: black; width: 50px;", mark, this.view, this.changeToFontSize));
         });
 
-        let newfontSizeDom = (new Dropdown(cut(fontSizeBtns), {
+        let newfontSizeDom = (new Dropdown(fontSizeBtns, {
             label: label,
             css: "color:black; min-width: 60px; padding-left: 5px; margin-right: 0;"
         }) as MenuItem).render(this.view).dom;
@@ -215,20 +196,15 @@ export class TooltipTextMenu {
         this.fontSizeDom = newfontSizeDom;
     }
 
-    // Make the DIV element draggable
-
     //label of dropdown will change to given label
     updateFontStyleDropdown(label: string) {
-        //filtering function - might be unecessary
-        let cut = (arr: MenuItem[]) => arr.filter(x => x);
-
         //font STYLES
         let fontBtns: MenuItem[] = [];
-        this.fontStylesToName.forEach((name, mark) => {
-            fontBtns.push(this.dropdownMarkBtn(name, "color: black; font-family: " + name + ", sans-serif; width: 125px;", mark, this.view, this.changeToMarkInGroup, this.fontStyles));
+        this.fontStyles.forEach((mark) => {
+            fontBtns.push(this.dropdownFontFamilyBtn(mark.attrs.family, "color: black; font-family: " + mark.attrs.family + ", sans-serif; width: 125px;", mark, this.view, this.changeToFontFamily));
         });
 
-        let newfontStyleDom = (new Dropdown(cut(fontBtns), {
+        let newfontStyleDom = (new Dropdown(fontBtns, {
             label: label,
             css: "color:black; width: 125px; margin-left: -3px; padding-left: 2px;"
         }) as MenuItem).render(this.view).dom;
@@ -468,8 +444,7 @@ export class TooltipTextMenu {
         this.tooltip.appendChild(listTypeBtn);
         return listTypeBtn;
     }
-
-    //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected text
+    //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected textchangeToMarkInGroup = (markType: MarkType | undefined, view: EditorView, fontMarks: MarkType[]) => {
     changeToMarkInGroup = (markType: MarkType | undefined, view: EditorView, fontMarks: MarkType[]) => {
         let { $cursor, ranges } = view.state.selection as TextSelection;
         let state = view.state;
@@ -498,29 +473,6 @@ export class TooltipTextMenu {
         });
 
         if (markType) {
-            // fontsize
-            if (markType.name[0] === 'p') {
-                let size = this.fontSizeToNum.get(markType);
-                if (size) { this.updateFontSizeDropdown(String(size) + " pt"); }
-                if (this.editorProps) {
-                    let ruleProvider = this.editorProps.ruleProvider;
-                    let heading = NumCast(this.editorProps.Document.heading);
-                    if (ruleProvider && heading) {
-                        ruleProvider["ruleSize_" + heading] = size;
-                    }
-                }
-            }
-            else {
-                let fontName = this.fontStylesToName.get(markType);
-                if (fontName) { this.updateFontStyleDropdown(fontName); }
-                if (this.editorProps) {
-                    let ruleProvider = this.editorProps.ruleProvider;
-                    let heading = NumCast(this.editorProps.Document.heading);
-                    if (ruleProvider && heading) {
-                        ruleProvider["ruleFont_" + heading] = fontName;
-                    }
-                }
-            }
             //actually apply font
             if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
                 let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
@@ -531,6 +483,90 @@ export class TooltipTextMenu {
         }
     }
 
+    changeToFontFamily = (mark: Mark, view: EditorView) => {
+        let { $cursor, ranges } = view.state.selection as TextSelection;
+        let state = view.state;
+        let dispatch = view.dispatch;
+
+        //remove all other active font marks
+        if ($cursor) {
+            if (view.state.schema.marks.pFontFamily.isInSet(state.storedMarks || $cursor.marks())) {
+                dispatch(state.tr.removeStoredMark(view.state.schema.marks.pFontFamily));
+            }
+        } else {
+            let has = false;
+            for (let i = 0; !has && i < ranges.length; i++) {
+                let { $from, $to } = ranges[i];
+                has = state.doc.rangeHasMark($from.pos, $to.pos, view.state.schema.marks.pFontFamily);
+            }
+            for (let i of ranges) {
+                if (has) {
+                    toggleMark(view.state.schema.marks.pFontFamily)(view.state, view.dispatch, view);
+                }
+            }
+        }
+
+        let fontName = mark.attrs.family;
+        if (fontName) { this.updateFontStyleDropdown(fontName); }
+        if (this.editorProps) {
+            let ruleProvider = this.editorProps.ruleProvider;
+            let heading = NumCast(this.editorProps.Document.heading);
+            if (ruleProvider && heading) {
+                ruleProvider["ruleFont_" + heading] = fontName;
+            }
+        }
+        //actually apply font
+        if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
+            let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
+                { ...(view.state.selection as NodeSelection).node.attrs, setFontFamily: fontName }), view.state.schema);
+            view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
+        }
+        else view.dispatch(view.state.tr.addMark(view.state.selection.from, view.state.selection.to, view.state.schema.marks.pFontFamily.create({ family: fontName })));
+        view.state.storedMarks = [...(view.state.storedMarks || []), view.state.schema.marks.pFontFamily.create({ family: fontName })];
+    }
+
+    changeToFontSize = (mark: Mark, view: EditorView) => {
+        let { $cursor, ranges } = view.state.selection as TextSelection;
+        let state = view.state;
+        let dispatch = view.dispatch;
+
+        //remove all other active font marks
+        if ($cursor) {
+            if (view.state.schema.marks.pFontSize.isInSet(state.storedMarks || $cursor.marks())) {
+                dispatch(state.tr.removeStoredMark(view.state.schema.marks.pFontSize));
+            }
+        } else {
+            let has = false;
+            for (let i = 0; !has && i < ranges.length; i++) {
+                let { $from, $to } = ranges[i];
+                has = state.doc.rangeHasMark($from.pos, $to.pos, view.state.schema.marks.pFontSize);
+            }
+            for (let i of ranges) {
+                if (has) {
+                    toggleMark(view.state.schema.marks.pFontSize)(view.state, view.dispatch, view);
+                }
+            }
+        }
+
+        let size = mark.attrs.fontSize;
+        if (size) { this.updateFontSizeDropdown(String(size) + " pt"); }
+        if (this.editorProps) {
+            let ruleProvider = this.editorProps.ruleProvider;
+            let heading = NumCast(this.editorProps.Document.heading);
+            if (ruleProvider && heading) {
+                ruleProvider["ruleSize_" + heading] = size;
+            }
+        }
+        //actually apply font
+        if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
+            let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
+                { ...(view.state.selection as NodeSelection).node.attrs, setFontSize: size }), view.state.schema);
+            view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
+        }
+        else view.dispatch(view.state.tr.addMark(view.state.selection.from, view.state.selection.to, view.state.schema.marks.pFontSize.create({ fontSize: size })));
+        view.state.storedMarks = [...(view.state.storedMarks || []), view.state.schema.marks.pFontSize.create({ fontSize: size })];
+    }
+
     //remove all node typeand apply the passed-in one to the selected text
     changeToNodeType = (nodeType: NodeType | undefined) => {
         //remove oldif (nodeType) { //add new
@@ -558,7 +594,7 @@ export class TooltipTextMenu {
 
     //makes a button for the drop down FOR MARKS
     //css is the style you want applied to the button
-    dropdownMarkBtn(label: string, css: string, markType: MarkType, view: EditorView, changeToMarkInGroup: (markType: MarkType<any>, view: EditorView, groupMarks: MarkType[]) => any, groupMarks: MarkType[]) {
+    dropdownFontFamilyBtn(label: string, css: string, mark: Mark, view: EditorView, changeFontFamily: (mark: Mark<any>, view: EditorView) => any) {
         return new MenuItem({
             title: "",
             label: label,
@@ -567,7 +603,22 @@ export class TooltipTextMenu {
             css: css,
             enable() { return true; },
             run() {
-                changeToMarkInGroup(markType, view, groupMarks);
+                changeFontFamily(mark, view);
+            }
+        });
+    }
+    //makes a button for the drop down FOR MARKS
+    //css is the style you want applied to the button
+    dropdownFontSizeBtn(label: string, css: string, mark: Mark, view: EditorView, changeFontSize: (markType: Mark<any>, view: EditorView) => any) {
+        return new MenuItem({
+            title: "",
+            label: label,
+            execEvent: "",
+            class: "menuicon",
+            css: css,
+            enable() { return true; },
+            run() {
+                changeFontSize(mark, view);
             }
         });
     }
@@ -649,7 +700,9 @@ export class TooltipTextMenu {
                         this.view.dispatch(this.view.state.tr.removeMark(from, to));
                         Array.from(this._brushMarks).filter(m => m.type !== schema.marks.user_mark).forEach((mark: Mark) => {
                             const markType = mark.type;
-                            this.changeToMarkInGroup(markType, this.view, []);
+                            if (mark.type === schema.marks.pFontFamily) this.changeToFontFamily(mark, this.view);
+                            else if (mark.type === schema.marks.pFontSize) this.changeToFontSize(mark, this.view);
+                            else this.changeToMarkInGroup(markType, this.view, []);
                         });
                     }
                 }
@@ -868,50 +921,23 @@ export class TooltipTextMenu {
         //UPDATE LIST ITEM DROPDOWN
 
         //UPDATE FONT STYLE DROPDOWN
-        let activeStyles = this.activeMarksOnSelection(this.fontStyles);
+        let activeStyles = this.activeFontFamilyOnSelection();
         if (activeStyles !== undefined) {
-            // activeStyles.forEach((markType) => {
-            //     this._activeMarks.push(this.view.state.schema.mark(markType));
-            // });
             if (activeStyles.length === 1) {
-                // if we want to update something somewhere with active font name
-                let fontName = this.fontStylesToName.get(activeStyles[0]);
-                if (fontName) { this.updateFontStyleDropdown(fontName); }
-            } else if (activeStyles.length === 0) {
-                //crimson on default
-                this.updateFontStyleDropdown("Crimson Text");
-            } else {
-                this.updateFontStyleDropdown("Various");
-            }
+                activeStyles[0] && this.updateFontStyleDropdown(activeStyles[0]);
+            } else this.updateFontStyleDropdown(activeStyles.length ? "various" : "default");
         }
 
         //UPDATE FONT SIZE DROPDOWN
-        let activeSizes = this.activeMarksOnSelection(this.fontSizes);
+        let activeSizes = this.activeFontSizeOnSelection();
         if (activeSizes !== undefined) {
             if (activeSizes.length === 1) { //if there's only one active font size
-                // activeSizes.forEach((markType) => {
-                //     this._activeMarks.push(this.view.state.schema.mark(markType));
-                // });
-                let size = this.fontSizeToNum.get(activeSizes[0]);
-                if (size) { this.updateFontSizeDropdown(String(size) + " pt"); }
-            } else if (activeSizes.length === 0) {
-                //should be 14 on default  
-                this.updateFontSizeDropdown("14 pt");
-            } else { //multiple font sizes selected
-                this.updateFontSizeDropdown("Various");
-            }
+                activeSizes[0] && this.updateFontSizeDropdown(String(activeSizes[0]) + " pt");
+            } else this.updateFontSizeDropdown(activeSizes.length ? "various" : "default");
         }
 
         this.update_mark_doms();
     }
-
-    public mark_key_pressed(marks: Mark<any>[]) {
-        if (this.view.state.selection.empty) {
-            if (marks) this._activeMarks = marks;
-            this.update_mark_doms();
-        }
-    }
-
     update_mark_doms() {
         this.reset_mark_doms();
         let foundlink = false;
@@ -946,6 +972,30 @@ export class TooltipTextMenu {
 
     }
 
+    //finds fontSize at start of selection
+    activeFontSizeOnSelection() {
+        //current selection
+        let state = this.view.state;
+        let activeSizes: number[] = [];
+        const pos = this.view.state.selection.$from;
+        const ref_node: ProsNode = this.reference_node(pos);
+        if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) {
+            ref_node.marks.forEach(m => m.type === state.schema.marks.pFontSize && activeSizes.push(m.attrs.fontSize));
+        }
+        return activeSizes;
+    }
+    //finds fontSize at start of selection
+    activeFontFamilyOnSelection() {
+        //current selection
+        let state = this.view.state;
+        let activeFamilies: string[] = [];
+        const pos = this.view.state.selection.$from;
+        const ref_node: ProsNode = this.reference_node(pos);
+        if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) {
+            ref_node.marks.forEach(m => m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family));
+        }
+        return activeFamilies;
+    }
     //finds all active marks on selection in given group
     activeMarksOnSelection(markGroup: MarkType[]) {
         //current selection
@@ -980,6 +1030,9 @@ export class TooltipTextMenu {
                 this._activeMarks = ref_node.marks;
                 activeMarks = markGroup.filter(mark_type => {
                     if (dispatch) {
+                        if (mark_type === state.schema.marks.pFontSize) {
+                            return ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name);
+                        }
                         let mark = state.schema.mark(mark_type);
                         return ref_node.marks.includes(mark);
                     }
-- 
cgit v1.2.3-70-g09d2

