aboutsummaryrefslogtreecommitdiff
path: root/src/client/util/TooltipTextMenu.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util/TooltipTextMenu.tsx')
-rw-r--r--src/client/util/TooltipTextMenu.tsx251
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;
}