diff options
Diffstat (limited to 'src/client/util/TooltipTextMenu.tsx')
-rw-r--r-- | src/client/util/TooltipTextMenu.tsx | 251 |
1 files changed, 182 insertions, 69 deletions
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index a4c053de2..b3b7848e8 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -1,4 +1,6 @@ import { action } from "mobx"; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faArrowUp, faTag, faPlus } from '@fortawesome/free-solid-svg-icons'; import { Dropdown, MenuItem, icons, } from "prosemirror-menu"; //no import css import { EditorState, NodeSelection, TextSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; @@ -18,7 +20,10 @@ import { DocServer } from "../DocServer"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { DocumentManager } from "./DocumentManager"; import { Id } from "../../new_fields/FieldSymbols"; -import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox"; +import { FormattedTextBoxProps, FormattedTextBox } from "../views/nodes/FormattedTextBox"; +import { typeAlias } from "babel-types"; +import React from "react"; +import ReactDOM from "react-dom"; import { Utils } from "../../Utils"; //appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc. @@ -33,6 +38,8 @@ export class TooltipTextMenu { private fontSizeToNum: Map<MarkType, number>; private fontStylesToName: Map<MarkType, string>; private listTypeToIcon: Map<NodeType, string>; + //private link: HTMLAnchorElement; + //private wrapper: HTMLDivElement; private linkEditor?: HTMLDivElement; private linkText?: HTMLDivElement; @@ -46,11 +53,19 @@ export class TooltipTextMenu { private _collapseBtn?: MenuItem; + private _brushMarks?: Set<Mark>; + private _brushIsEmpty: boolean = true; + private _brushdom?: Node; + + private _marksToDoms: Map<Mark, HTMLSpanElement> = new Map(); + constructor(view: EditorView, editorProps: FieldViewProps & FormattedTextBoxProps) { this.view = view; this.editorProps = editorProps; + //this.wrapper = document.createElement("div"); this.tooltip = document.createElement("div"); this.tooltip.className = "tooltipMenu"; + this.dragElement(this.tooltip); this.dragElement(this.tooltip); // this.createCollapse(); @@ -71,13 +86,23 @@ export class TooltipTextMenu { { command: toggleMark(schema.marks.superscript), dom: this.icon("s", "superscript", "Superscript") }, { command: toggleMark(schema.marks.subscript), dom: this.icon("s", "subscript", "Subscript") }, { command: toggleMark(schema.marks.highlight), dom: this.icon("H", 'blue', 'Blue') } - // { command: wrapInList(schema.nodes.bullet_list), dom: this.icon(":", "bullets") }, - // { command: wrapInList(schema.nodes.ordered_list), dom: this.icon("1)", "bullets") }, - // { command: lift, dom: this.icon("<", "lift") }, ]; + + this._marksToDoms = new Map(); //add menu items items.forEach(({ dom, command }) => { this.tooltip.appendChild(dom); + switch (dom.title) { + case "Bold": + this._marksToDoms.set(schema.mark(schema.marks.strong), dom); + break; + case "Italic": + this._marksToDoms.set(schema.mark(schema.marks.em), dom); + break; + case "Underline": + this._marksToDoms.set(schema.mark(schema.marks.underline), dom); + break; + } //pointer down handler to activate button effects dom.addEventListener("pointerdown", e => { @@ -86,12 +111,17 @@ export class TooltipTextMenu { if (dom.contains(e.target as Node)) { e.stopPropagation(); command(view.state, view.dispatch, view); + if (this.view.state.selection.empty) { + if (dom.style.color === "white") { dom.style.color = "greenyellow"; } + else { dom.style.color = "white"; } + } } }); }); this.updateLinkMenu(); + //list of font styles this.fontStylesToName = new Map(); this.fontStylesToName.set(schema.marks.timesNewRoman, "Times New Roman"); @@ -125,19 +155,21 @@ export class TooltipTextMenu { this.listTypeToIcon.set(schema.nodes.ordered_list, "1)"); this.listTypes = Array.from(this.listTypeToIcon.keys()); + //custom tools // this.tooltip.appendChild(this.createLink().render(this.view).dom); + this._brushdom = this.createBrush().render(this.view).dom; + this.tooltip.appendChild(this._brushdom); + this.tooltip.appendChild(this.createLink().render(this.view).dom); this.tooltip.appendChild(this.createStar().render(this.view).dom); - - this.updateListItemDropdown(":", this.listTypeBtnDom); this.update(view, undefined); //view.dom.parentNode!.parentNode!.insertBefore(this.tooltip, view.dom.parentNode); - // quick and dirty null check + // add tooltip to outerdiv to circumvent scaling problem const outer_div = this.editorProps.outer_div; outer_div && outer_div(this.tooltip); } @@ -164,6 +196,49 @@ export class TooltipTextMenu { this.fontSizeDom = newfontSizeDom; } + // Make the DIV element draggable: + + dragElement(elmnt: HTMLElement) { + var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; + if (elmnt) { + // if present, the header is where you move the DIV from: + elmnt.onpointerdown = dragMouseDown; + } + const self = this; + + function dragMouseDown(e: PointerEvent) { + e = e || window.event; + //e.preventDefault(); + // get the mouse cursor position at startup: + pos3 = e.clientX; + pos4 = e.clientY; + document.onpointerup = closeDragElement; + // call a function whenever the cursor moves: + document.onpointermove = elementDrag; + } + + function elementDrag(e: PointerEvent) { + e = e || window.event; + //e.preventDefault(); + // calculate the new cursor position: + pos1 = pos3 - e.clientX; + pos2 = pos4 - e.clientY; + pos3 = e.clientX; + pos4 = e.clientY; + // set the element's new position: + elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; + elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; + } + + function closeDragElement() { + // stop moving when mouse button is released: + document.onpointerup = null; + document.onpointermove = null; + //self.highlightSearchTerms(self.state, ["hello"]); + //FormattedTextBox.Instance.unhighlightSearchTerms(); + } + } + //label of dropdown will change to given label updateFontStyleDropdown(label: string) { //filtering function - might be unecessary @@ -259,6 +334,8 @@ export class TooltipTextMenu { }, hideSource: false }); + e.stopPropagation(); + e.preventDefault(); }; this.linkEditor.appendChild(this.linkDrag); // this.linkEditor.appendChild(this.linkText); @@ -280,47 +357,6 @@ export class TooltipTextMenu { // this.tooltip.appendChild(this.linkEditor); } - dragElement(elmnt: HTMLElement) { - var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; - if (elmnt) { - // if present, the header is where you move the DIV from: - elmnt.onpointerdown = dragMouseDown; - } - const self = this; - - function dragMouseDown(e: PointerEvent) { - e = e || window.event; - //e.preventDefault(); - // get the mouse cursor position at startup: - pos3 = e.clientX; - pos4 = e.clientY; - document.onpointerup = closeDragElement; - // call a function whenever the cursor moves: - document.onpointermove = elementDrag; - } - - function elementDrag(e: PointerEvent) { - e = e || window.event; - //e.preventDefault(); - // calculate the new cursor position: - pos1 = pos3 - e.clientX; - pos2 = pos4 - e.clientY; - pos3 = e.clientX; - pos4 = e.clientY; - // set the element's new position: - elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; - elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; - } - - function closeDragElement() { - // stop moving when mouse button is released: - document.onpointerup = null; - document.onpointermove = null; - //self.highlightSearchTerms(self.state, ["hello"]); - //FormattedTextBox.Instance.unhighlightSearchTerms(); - } - } - makeLink = (target: string, location: string) => { 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 }); @@ -363,7 +399,7 @@ export class TooltipTextMenu { } //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected text - changeToMarkInGroup = (markType: MarkType, view: EditorView, fontMarks: MarkType[]) => { + changeToMarkInGroup = (markType: MarkType | undefined, view: EditorView, fontMarks: MarkType[]) => { let { $cursor, ranges } = view.state.selection as TextSelection; let state = view.state; let dispatch = view.dispatch; @@ -389,17 +425,23 @@ export class TooltipTextMenu { } } }); - // fontsize - if (markType.name[0] === 'p') { - let size = this.fontSizeToNum.get(markType); - if (size) { this.updateFontSizeDropdown(String(size) + " pt"); } + + if (markType) { + // fontsize + if (markType.name[0] === 'p') { + let size = this.fontSizeToNum.get(markType); + if (size) { this.updateFontSizeDropdown(String(size) + " pt"); } + } + else { + let fontName = this.fontStylesToName.get(markType); + if (fontName) { this.updateFontStyleDropdown(fontName); } + } + //actually apply font + return toggleMark(markType)(view.state, view.dispatch, view); } else { - let fontName = this.fontStylesToName.get(markType); - if (fontName) { this.updateFontStyleDropdown(fontName); } + return; } - //actually apply font - return toggleMark(markType)(view.state, view.dispatch, view); } //remove all node typeand apply the passed-in one to the selected text @@ -442,6 +484,62 @@ export class TooltipTextMenu { }); } + createBrush(active: boolean = false) { + const icon = { + height: 32, width: 32, + path: "M30.828 1.172c-1.562-1.562-4.095-1.562-5.657 0l-5.379 5.379-3.793-3.793-4.243 4.243 3.326 3.326-14.754 14.754c-0.252 0.252-0.358 0.592-0.322 0.921h-0.008v5c0 0.552 0.448 1 1 1h5c0 0 0.083 0 0.125 0 0.288 0 0.576-0.11 0.795-0.329l14.754-14.754 3.326 3.326 4.243-4.243-3.793-3.793 5.379-5.379c1.562-1.562 1.562-4.095 0-5.657zM5.409 30h-3.409v-3.409l14.674-14.674 3.409 3.409-14.674 14.674z" + }; + return new MenuItem({ + title: "Brush tool", + label: "Brush tool", + icon: icon, + css: "color:white;", + class: active ? "brush-active" : "brush", + execEvent: "", + run: (state, dispatch) => { + this.brush_function(state, dispatch); + }, + active: (state) => { + return true; + } + }); + } + + // selectionchanged event handler + + brush_function(state: EditorState<any>, dispatch: any) { + if (this._brushIsEmpty) { + const selected_marks = this.getMarksInSelection(this.view.state); + if (selected_marks.size > 0 && this._brushdom) { + this._brushMarks = selected_marks; + const newbrush = this.createBrush(true).render(this.view).dom; + this.tooltip.replaceChild(newbrush, this._brushdom); + this._brushdom = newbrush; + this._brushIsEmpty = !this._brushIsEmpty; + } + } + else { + let { from, to, $from } = this.view.state.selection; + if ($from && $from.nodeAfter && this._brushdom) { + if (this._brushMarks && to - from > 0) { + this.view.dispatch(this.view.state.tr.removeMark(from, to)); + this._brushMarks.forEach((mark: Mark) => { + const markType = mark.type; + this.changeToMarkInGroup(markType, this.view, []); + + }); + } + + const newbrush = this.createBrush(false).render(this.view).dom; + this.tooltip.replaceChild(newbrush, this._brushdom); + this._brushdom = newbrush; + this._brushIsEmpty = !this._brushIsEmpty; + } + } + + + } + createCollapse() { this._collapseBtn = new MenuItem({ title: "Collapse", @@ -597,14 +695,14 @@ export class TooltipTextMenu { }; } - getMarksInSelection(state: EditorState<any>, targets: MarkType<any>[]) { - let found: Mark<any>[] = []; + getMarksInSelection(state: EditorState<any>) { + let found = new Set<Mark>(); let { from, to } = state.selection as TextSelection; state.doc.nodesBetween(from, to, (node) => { let marks = node.marks; if (marks) { marks.forEach(m => { - if (targets.includes(m.type)) found.push(m); + found.add(m); }); } }); @@ -618,13 +716,18 @@ export class TooltipTextMenu { if (lastState && lastState.doc.eq(state.doc) && lastState.selection.eq(state.selection)) return; + let iterator = this._marksToDoms.values(); + let next = iterator.next(); + while (!next.done) { + next.value.style.color = "white"; + next = iterator.next(); + } + // Hide the tooltip if the selection is empty if (state.selection.empty) { //this.tooltip.style.display = "none"; //return; } - - //UPDATE LIST ITEM DROPDOWN //UPDATE FONT STYLE DROPDOWN @@ -662,12 +765,18 @@ export class TooltipTextMenu { } } this.view.dispatch(this.view.state.tr.setStoredMarks(this._activeMarks)); + this._activeMarks.forEach((mark) => { + if (this._marksToDoms.has(mark)) { + let dom = this._marksToDoms.get(mark); + if (dom) dom.style.color = "greenyellow"; + } + }); } //finds all active marks on selection in given group activeMarksOnSelection(markGroup: MarkType[]) { //current selection - let { empty, ranges } = this.view.state.selection as TextSelection; + let { empty, ranges, $to } = this.view.state.selection as TextSelection; let state = this.view.state; let dispatch = this.view.dispatch; let activeMarks: MarkType[]; @@ -682,6 +791,9 @@ export class TooltipTextMenu { } return false; }); + + const refnode = this.reference_node($to); + this._activeMarks = refnode.marks; } else { const pos = this.view.state.selection.$from; @@ -692,9 +804,7 @@ export class TooltipTextMenu { else { return []; } - this._activeMarks = ref_node.marks; - activeMarks = markGroup.filter(mark_type => { if (dispatch) { let mark = state.schema.mark(mark_type); @@ -713,12 +823,12 @@ export class TooltipTextMenu { reference_node(pos: ResolvedPos<any>): ProsNode { let ref_node: ProsNode = this.view.state.doc; - if (pos.nodeAfter !== null && pos.nodeAfter !== undefined) { - ref_node = pos.nodeAfter; - } - else if (pos.nodeBefore !== null && pos.nodeBefore !== undefined) { + if (pos.nodeBefore !== null && pos.nodeBefore !== undefined) { ref_node = pos.nodeBefore; } + else if (pos.nodeAfter !== null && pos.nodeAfter !== undefined) { + ref_node = pos.nodeAfter; + } else if (pos.pos > 0) { let skip = false; for (let i: number = pos.pos - 1; i > 0; i--) { @@ -731,6 +841,9 @@ export class TooltipTextMenu { }); } } + if (!ref_node.isLeaf) { + ref_node = ref_node.child(0); + } return ref_node; } |