diff options
Diffstat (limited to 'src/client/util')
| -rw-r--r-- | src/client/util/RichTextSchema.tsx | 11 | ||||
| -rw-r--r-- | src/client/util/TooltipTextMenu.scss | 57 | ||||
| -rw-r--r-- | src/client/util/TooltipTextMenu.tsx | 251 | ||||
| -rw-r--r-- | src/client/util/request-image-size.js | 2 |
4 files changed, 220 insertions, 101 deletions
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 269de0f42..ce9e29b26 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -283,7 +283,7 @@ export const marks: { [index: string]: MarkSpec } = { }, highlight: { - parseDOM: [{ style: 'background: #d9dbdd' }], + parseDOM: [{ style: 'color: blue' }], toDOM() { return ['span', { style: 'color: blue' @@ -291,6 +291,15 @@ export const marks: { [index: string]: MarkSpec } = { } }, + search_highlight: { + parseDOM: [{ style: 'background: yellow' }], + toDOM() { + return ['span', { + style: 'background: yellow' + }]; + } + }, + // :: MarkSpec Code font mark. Represented as a `<code>` element. code: { diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss index 40ac3abb9..864c17cac 100644 --- a/src/client/util/TooltipTextMenu.scss +++ b/src/client/util/TooltipTextMenu.scss @@ -18,7 +18,8 @@ .ProseMirror-menuitem { margin-right: 3px; display: inline-block; - z-index: 100000; + z-index: 50000; + position: relative; } .ProseMirror-menuseparator { @@ -67,7 +68,7 @@ } .ProseMirror-menu-dropdown-menu { - z-index: 100000; + z-index: 50000; min-width: 6em; background: white; position: absolute; @@ -235,8 +236,8 @@ } .tooltipMenu { - position: relative; - z-index: 2000; + position: absolute; + z-index: 20000; background: #121721; border: 1px solid silver; border-radius: 15px; @@ -247,7 +248,7 @@ //transform: translateX(-50%); transform: translateY(-85px); pointer-events: all; - height: 30px; + height: fit-content; width:550px; .ProseMirror-example-setup-style hr { padding: 2px 10px; @@ -264,29 +265,6 @@ } } -// .tooltipMenu:before { -// content: ""; -// height: 0; width: 0; -// position: absolute; -// left: 50%; -// margin-left: -5px; -// bottom: -6px; -// border: 5px solid transparent; -// border-bottom-width: 0; -// border-top-color: silver; -// } -// .tooltipMenu:after { -// content: ""; -// height: 0; width: 0; -// position: absolute; -// left: 50%; -// margin-left: -5px; -// bottom: -4.5px; -// border: 5px solid transparent; -// border-bottom-width: 0; -// border-top-color: $dark-color; -// } - .menuicon { display: inline-block; border-right: 1px solid white(0, 0, 0, 0.2); @@ -298,6 +276,7 @@ cursor: pointer; text-align: center; min-width: 10px; + } .strong, .heading { font-weight: bold; } .em { font-style: italic; } @@ -310,9 +289,27 @@ padding-right: 0px; } .summarize{ - //margin-left: 15px; color: white; height: 20px; - // background-color: white; text-align: center; + } + + .brush{ + display: inline-block; + width: 1em; + height: 1em; + stroke-width: 0; + stroke: currentColor; + fill: currentColor; + margin-right: 15px; + } + + .brush-active{ + display: inline-block; + width: 1em; + height: 1em; + stroke-width: 3; + stroke: greenyellow; + fill: greenyellow; + margin-right: 15px; }
\ No newline at end of file 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; } diff --git a/src/client/util/request-image-size.js b/src/client/util/request-image-size.js index 27605d167..257990811 100644 --- a/src/client/util/request-image-size.js +++ b/src/client/util/request-image-size.js @@ -10,7 +10,7 @@ */ const request = require('request'); -const imageSize = require('image-size'); +// const imageSize = require('image-size'); const HttpError = require('standard-http-error'); module.exports = function requestImageSize(options) { |
