diff options
Diffstat (limited to 'src/client/util/TooltipTextMenu.tsx')
-rw-r--r-- | src/client/util/TooltipTextMenu.tsx | 1216 |
1 files changed, 368 insertions, 848 deletions
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 35a099c48..1c15dca7f 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -1,17 +1,13 @@ -import { action } from "mobx"; import { Dropdown, icons, MenuItem } from "prosemirror-menu"; //no import css import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos, Schema } from "prosemirror-model"; import { wrapInList } from 'prosemirror-schema-list'; import { EditorState, NodeSelection, TextSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { Doc, Field, Opt } from "../../new_fields/Doc"; -import { Id } from "../../new_fields/FieldSymbols"; import { Utils } from "../../Utils"; import { DocServer } from "../DocServer"; import { FieldViewProps } from "../views/nodes/FieldView"; import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox"; -import { DocumentManager } from "./DocumentManager"; -import { DragManager } from "./DragManager"; import { LinkManager } from "./LinkManager"; import { schema } from "./RichTextSchema"; import "./TooltipTextMenu.scss"; @@ -20,12 +16,10 @@ import { updateBullets } from './ProsemirrorExampleTransfer'; import { DocumentDecorations } from '../views/DocumentDecorations'; import { SelectionManager } from './SelectionManager'; import { PastelSchemaPalette, DarkPastelSchemaPalette } from '../../new_fields/SchemaHeaderField'; -const { toggleMark, setBlockType } = require("prosemirror-commands"); -const { openPrompt, TextField } = require("./ProsemirrorCopy/prompt.js"); +const { toggleMark } = require("prosemirror-commands"); //appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc. export class TooltipTextMenu { - public static Toolbar: HTMLDivElement | undefined; // editor state properties @@ -34,10 +28,7 @@ export class TooltipTextMenu { private fontStyles: Mark[] = []; private fontSizes: Mark[] = []; - private listTypes: (NodeType | any)[] = []; - private listTypeToIcon: Map<NodeType | any, string> = new Map(); - private _activeMarks: Mark[] = []; - private _marksToDoms: Map<Mark, HTMLSpanElement> = new Map(); + private _marksToDoms: Map<MarkType, HTMLSpanElement> = new Map(); private _collapsed: boolean = false; // editor doms @@ -47,20 +38,18 @@ export class TooltipTextMenu { // editor button doms private colorDom?: Node; private colorDropdownDom?: Node; - private highlightDom?: Node; - private highlightDropdownDom?: Node; - private linkEditor?: HTMLDivElement; - private linkText?: HTMLDivElement; - private linkDrag?: HTMLImageElement; - private _linkDropdownDom?: Node; + private linkDom?: Node; + private highighterDom?: Node; + private highlighterDropdownDom?: Node; + private linkDropdownDom?: Node; private _brushdom?: Node; private _brushDropdownDom?: Node; private fontSizeDom?: Node; private fontStyleDom?: Node; - private listTypeBtnDom?: Node; private basicTools?: HTMLElement; - + static createDiv(className: string) { const div = document.createElement("div"); div.className = className; return div; } + static createSpan(className: string) { const div = document.createElement("span"); div.className = className; return div; } constructor(view: EditorView) { this.view = view; @@ -68,74 +57,64 @@ export class TooltipTextMenu { this.initTooltip(view); // initialize the wrapper - this.wrapper = document.createElement("div"); - this.wrapper.className = "wrapper"; + this.wrapper = TooltipTextMenu.createDiv("wrapper"); this.wrapper.appendChild(this.tooltip); - // initialize the dragger -- appends it to the wrapper - this.createDragger(); - TooltipTextMenu.Toolbar = this.wrapper; } private async initTooltip(view: EditorView) { - // initialize tooltip dom - this.tooltip = document.createElement("div"); - this.tooltip.className = "tooltipMenu"; - this.basicTools = document.createElement("div"); - this.basicTools.className = "basic-tools"; - - // init buttons to the tooltip -- paths to svgs are obtained from fontawesome - const items = [ - { command: toggleMark(schema.marks.strong), dom: this.svgIcon("strong", "Bold", "M333.49 238a122 122 0 0 0 27-65.21C367.87 96.49 308 32 233.42 32H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h31.87v288H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h209.32c70.8 0 134.14-51.75 141-122.4 4.74-48.45-16.39-92.06-50.83-119.6zM145.66 112h87.76a48 48 0 0 1 0 96h-87.76zm87.76 288h-87.76V288h87.76a56 56 0 0 1 0 112z") }, - { command: toggleMark(schema.marks.em), dom: this.svgIcon("em", "Italic", "M320 48v32a16 16 0 0 1-16 16h-62.76l-80 320H208a16 16 0 0 1 16 16v32a16 16 0 0 1-16 16H16a16 16 0 0 1-16-16v-32a16 16 0 0 1 16-16h62.76l80-320H112a16 16 0 0 1-16-16V48a16 16 0 0 1 16-16h192a16 16 0 0 1 16 16z") }, - { command: toggleMark(schema.marks.underline), dom: this.svgIcon("underline", "Underline", "M32 64h32v160c0 88.22 71.78 160 160 160s160-71.78 160-160V64h32a16 16 0 0 0 16-16V16a16 16 0 0 0-16-16H272a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h32v160a80 80 0 0 1-160 0V64h32a16 16 0 0 0 16-16V16a16 16 0 0 0-16-16H32a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16zm400 384H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z") }, - { command: toggleMark(schema.marks.strikethrough), dom: this.svgIcon("strikethrough", "Strikethrough", "M496 224H293.9l-87.17-26.83A43.55 43.55 0 0 1 219.55 112h66.79A49.89 49.89 0 0 1 331 139.58a16 16 0 0 0 21.46 7.15l42.94-21.47a16 16 0 0 0 7.16-21.46l-.53-1A128 128 0 0 0 287.51 32h-68a123.68 123.68 0 0 0-123 135.64c2 20.89 10.1 39.83 21.78 56.36H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h480a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm-180.24 96A43 43 0 0 1 336 356.45 43.59 43.59 0 0 1 292.45 400h-66.79A49.89 49.89 0 0 1 181 372.42a16 16 0 0 0-21.46-7.15l-42.94 21.47a16 16 0 0 0-7.16 21.46l.53 1A128 128 0 0 0 224.49 480h68a123.68 123.68 0 0 0 123-135.64 114.25 114.25 0 0 0-5.34-24.36z") }, - { command: toggleMark(schema.marks.superscript), dom: this.svgIcon("superscript", "Superscript", "M496 160h-16V16a16 16 0 0 0-16-16h-48a16 16 0 0 0-14.29 8.83l-16 32A16 16 0 0 0 400 64h16v96h-16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h96a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM336 64h-67a16 16 0 0 0-13.14 6.87l-79.9 115-79.9-115A16 16 0 0 0 83 64H16A16 16 0 0 0 0 80v48a16 16 0 0 0 16 16h33.48l77.81 112-77.81 112H16a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h67a16 16 0 0 0 13.14-6.87l79.9-115 79.9 115A16 16 0 0 0 269 448h67a16 16 0 0 0 16-16v-48a16 16 0 0 0-16-16h-33.48l-77.81-112 77.81-112H336a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16z") }, - { command: toggleMark(schema.marks.subscript), dom: this.svgIcon("subscript", "Subscript", "M496 448h-16V304a16 16 0 0 0-16-16h-48a16 16 0 0 0-14.29 8.83l-16 32A16 16 0 0 0 400 352h16v96h-16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h96a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM336 64h-67a16 16 0 0 0-13.14 6.87l-79.9 115-79.9-115A16 16 0 0 0 83 64H16A16 16 0 0 0 0 80v48a16 16 0 0 0 16 16h33.48l77.81 112-77.81 112H16a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h67a16 16 0 0 0 13.14-6.87l79.9-115 79.9 115A16 16 0 0 0 269 448h67a16 16 0 0 0 16-16v-48a16 16 0 0 0-16-16h-33.48l-77.81-112 77.81-112H336a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16z") }, - // { command: toggleMark(schema.marks.highlight), dom: this.icon("H", 'blue', 'Blue') } + const self = this; + this.tooltip = TooltipTextMenu.createDiv("tooltipMenu"); + this.basicTools = TooltipTextMenu.createDiv("basic-tools"); + + const svgIcon = (name: string, title: string = name, dpath: string) => { + const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + svg.setAttribute("viewBox", "-100 -100 650 650"); + const path = document.createElementNS('http://www.w3.org/2000/svg', "path"); + path.setAttributeNS(null, "d", dpath); + svg.appendChild(path); + + const span = TooltipTextMenu.createSpan(name + " menuicon"); + span.title = title; + span.appendChild(svg); + + return span; + }; + + const basicItems = [ // init basicItems in minimized toolbar -- paths to svgs are obtained from fontawesome + { mark: schema.marks.strong, dom: svgIcon("strong", "Bold", "M333.49 238a122 122 0 0 0 27-65.21C367.87 96.49 308 32 233.42 32H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h31.87v288H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h209.32c70.8 0 134.14-51.75 141-122.4 4.74-48.45-16.39-92.06-50.83-119.6zM145.66 112h87.76a48 48 0 0 1 0 96h-87.76zm87.76 288h-87.76V288h87.76a56 56 0 0 1 0 112z") }, + { mark: schema.marks.em, dom: svgIcon("em", "Italic", "M320 48v32a16 16 0 0 1-16 16h-62.76l-80 320H208a16 16 0 0 1 16 16v32a16 16 0 0 1-16 16H16a16 16 0 0 1-16-16v-32a16 16 0 0 1 16-16h62.76l80-320H112a16 16 0 0 1-16-16V48a16 16 0 0 1 16-16h192a16 16 0 0 1 16 16z") }, + { mark: schema.marks.underline, dom: svgIcon("underline", "Underline", "M32 64h32v160c0 88.22 71.78 160 160 160s160-71.78 160-160V64h32a16 16 0 0 0 16-16V16a16 16 0 0 0-16-16H272a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h32v160a80 80 0 0 1-160 0V64h32a16 16 0 0 0 16-16V16a16 16 0 0 0-16-16H32a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16zm400 384H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z") }, + ]; + const items = [ // init items in full size toolbar + { mark: schema.marks.strikethrough, dom: svgIcon("strikethrough", "Strikethrough", "M496 224H293.9l-87.17-26.83A43.55 43.55 0 0 1 219.55 112h66.79A49.89 49.89 0 0 1 331 139.58a16 16 0 0 0 21.46 7.15l42.94-21.47a16 16 0 0 0 7.16-21.46l-.53-1A128 128 0 0 0 287.51 32h-68a123.68 123.68 0 0 0-123 135.64c2 20.89 10.1 39.83 21.78 56.36H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h480a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm-180.24 96A43 43 0 0 1 336 356.45 43.59 43.59 0 0 1 292.45 400h-66.79A49.89 49.89 0 0 1 181 372.42a16 16 0 0 0-21.46-7.15l-42.94 21.47a16 16 0 0 0-7.16 21.46l.53 1A128 128 0 0 0 224.49 480h68a123.68 123.68 0 0 0 123-135.64 114.25 114.25 0 0 0-5.34-24.36z") }, + { mark: schema.marks.superscript, dom: svgIcon("superscript", "Superscript", "M496 160h-16V16a16 16 0 0 0-16-16h-48a16 16 0 0 0-14.29 8.83l-16 32A16 16 0 0 0 400 64h16v96h-16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h96a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM336 64h-67a16 16 0 0 0-13.14 6.87l-79.9 115-79.9-115A16 16 0 0 0 83 64H16A16 16 0 0 0 0 80v48a16 16 0 0 0 16 16h33.48l77.81 112-77.81 112H16a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h67a16 16 0 0 0 13.14-6.87l79.9-115 79.9 115A16 16 0 0 0 269 448h67a16 16 0 0 0 16-16v-48a16 16 0 0 0-16-16h-33.48l-77.81-112 77.81-112H336a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16z") }, + { mark: schema.marks.subscript, dom: svgIcon("subscript", "Subscript", "M496 448h-16V304a16 16 0 0 0-16-16h-48a16 16 0 0 0-14.29 8.83l-16 32A16 16 0 0 0 400 352h16v96h-16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h96a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM336 64h-67a16 16 0 0 0-13.14 6.87l-79.9 115-79.9-115A16 16 0 0 0 83 64H16A16 16 0 0 0 0 80v48a16 16 0 0 0 16 16h33.48l77.81 112-77.81 112H16a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h67a16 16 0 0 0 13.14-6.87l79.9-115 79.9 115A16 16 0 0 0 269 448h67a16 16 0 0 0 16-16v-48a16 16 0 0 0-16-16h-33.48l-77.81-112 77.81-112H336a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16z") }, ]; - // add menu items - this._marksToDoms = new Map(); - items.forEach(({ dom, command }) => { + basicItems.map(({ dom, mark }) => this.basicTools ?.appendChild(dom.cloneNode(true))); + basicItems.concat(items).forEach(({ dom, mark }) => { this.tooltip.appendChild(dom); - switch (dom.title) { - case "Bold": - this._marksToDoms.set(schema.mark(schema.marks.strong), dom); - this.basicTools && this.basicTools.appendChild(dom.cloneNode(true)); - break; - case "Italic": - this._marksToDoms.set(schema.mark(schema.marks.em), dom); - this.basicTools && this.basicTools.appendChild(dom.cloneNode(true)); - break; - case "Underline": - this._marksToDoms.set(schema.mark(schema.marks.underline), dom); - this.basicTools && this.basicTools.appendChild(dom.cloneNode(true)); - break; - } + this._marksToDoms.set(mark, dom); //pointer down handler to activate button effects dom.addEventListener("pointerdown", e => { - e.preventDefault(); this.view.focus(); if (dom.contains(e.target as Node)) { + e.preventDefault(); e.stopPropagation(); - command(this.view.state, this.view.dispatch, this.view); - // if (this.view.state.selection.empty) { - // if (dom.style.color === "white") { dom.style.color = "greenyellow"; } - // else { dom.style.color = "white"; } - // } + toggleMark(mark)(this.view.state, this.view.dispatch, this.view); + this.updateHighlightStateOfButtons(); } }); - }); - // highlight menu - this.highlightDom = this.createHighlightTool().render(this.view).dom; - this.highlightDropdownDom = this.createHighlightDropdown().render(this.view).dom; - this.tooltip.appendChild(this.highlightDom); - this.tooltip.appendChild(this.highlightDropdownDom); + // summarize menu + this.highighterDom = this.createHighlightTool().render(this.view).dom; + this.highlighterDropdownDom = this.createHighlightDropdown().render(this.view).dom; + this.tooltip.appendChild(this.highighterDom); + this.tooltip.appendChild(this.highlighterDropdownDom); // color menu this.colorDom = this.createColorTool().render(this.view).dom; @@ -144,46 +123,12 @@ export class TooltipTextMenu { this.tooltip.appendChild(this.colorDropdownDom); // link menu - this.updateLinkMenu(); - const dropdown = await this.createLinkDropdown(); - this._linkDropdownDom = dropdown.render(this.view).dom; - this.tooltip.appendChild(this._linkDropdownDom); + this.linkDom = this.createLinkTool().render(this.view).dom; + this.linkDropdownDom = this.createLinkDropdown("").render(this.view).dom; + this.tooltip.appendChild(this.linkDom); + this.tooltip.appendChild(this.linkDropdownDom); // list of font styles - this.initFontStyles(); - - // font sizes - this.initFontSizes(); - - // list types - this.initListTypes(); - - // init brush tool - this._brushdom = this.createBrush().render(this.view).dom; - this.tooltip.appendChild(this._brushdom); - this._brushDropdownDom = this.createBrushDropdown().render(this.view).dom; - this.tooltip.appendChild(this._brushDropdownDom); - - // star - this.tooltip.appendChild(this.createStar().render(this.view).dom); - - // list types dropdown - this.updateListItemDropdown(":", this.listTypeBtnDom); - - await this.updateFromDash(view, undefined, undefined); - } - - initFontStyles() { - 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" })); - } - - initFontSizes() { this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 7 })); this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 8 })); this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 9 })); @@ -197,56 +142,89 @@ export class TooltipTextMenu { 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 })); - } - initListTypes() { - 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.bullet_list, "⬜"); - this.listTypes = Array.from(this.listTypeToIcon.keys()); - } + // font sizes + 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" })); - // creates dragger element that allows dragging and collapsing (on double click) - // of editor and appends it to the wrapper - createDragger() { - const draggerWrapper = document.createElement("div"); - draggerWrapper.className = "dragger-wrapper"; - const dragger = document.createElement("div"); - dragger.className = "dragger"; + // init brush tool + this._brushdom = this.createBrushTool().render(this.view).dom; + this.tooltip.appendChild(this._brushdom); + this._brushDropdownDom = this.createBrushDropdown().render(this.view).dom; + this.tooltip.appendChild(this._brushDropdownDom); - const line1 = document.createElement("span"); - line1.className = "dragger-line"; - const line2 = document.createElement("span"); - line2.className = "dragger-line"; - const line3 = document.createElement("span"); - line3.className = "dragger-line"; + // summarizer tool + const summarizer = new MenuItem({ + title: "Summarize", + label: "Summarize", + icon: icons.join, + css: "fill:white;", + class: "menuicon", + execEvent: "", + run: (state, dispatch) => TooltipTextMenu.insertSummarizer(state, dispatch) + }); + this.tooltip.appendChild(summarizer.render(this.view).dom); - dragger.appendChild(line1); - dragger.appendChild(line2); - dragger.appendChild(line3); + // list types dropdown + const listDropdownTypes = [{ mapStyle: "bullet", label: ":" }, { mapStyle: "decimal", label: "1.1" }, { mapStyle: "multi", label: "A.1" }, { label: "X" }]; + const listTypes = new Dropdown(listDropdownTypes.map(({ mapStyle, label }) => + new MenuItem({ + title: "Set Bullet Style", + label: label, + execEvent: "", + class: "dropdown-item", + css: "color: black; width: 40px;", + enable() { return true; }, + run() { + const marks = self.view.state.storedMarks || (view.state.selection.$to.parentOffset && view.state.selection.$from.marks()); + if (!wrapInList(schema.nodes.ordered_list)(view.state, (tx2: any) => { + const tx3 = updateBullets(tx2, schema, mapStyle); + marks && tx3.ensureMarks([...marks]); + marks && tx3.setStoredMarks([...marks]); + + view.dispatch(tx2); + })) { + const tx2 = view.state.tr; + const tx3 = updateBullets(tx2, schema, mapStyle); + marks && tx3.ensureMarks([...marks]); + marks && tx3.setStoredMarks([...marks]); + + view.dispatch(tx3); + } + } + })), { label: ":", css: "color:black; width: 40px;" }); + this.tooltip.appendChild(listTypes.render(this.view).dom); - draggerWrapper.appendChild(dragger); + await this.updateFromDash(view, undefined, undefined); + const draggerWrapper = TooltipTextMenu.createDiv("dragger-wrapper"); + const dragger = TooltipTextMenu.createDiv("dragger"); + dragger.appendChild(TooltipTextMenu.createSpan("dragger-line")); + dragger.appendChild(TooltipTextMenu.createSpan("dragger-line")); + dragger.appendChild(TooltipTextMenu.createSpan("dragger-line")); + draggerWrapper.appendChild(dragger); this.wrapper.appendChild(draggerWrapper); - this.dragElement(draggerWrapper); + this.setupDragElementInteractions(draggerWrapper); } - dragElement(elmnt: HTMLElement) { + setupDragElementInteractions(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; + elmnt.onpointerdown = dragPointerDown; elmnt.ondblclick = onClick; } const self = this; - function dragMouseDown(e: PointerEvent) { + function dragPointerDown(e: PointerEvent) { e = e || window.event; - //e.preventDefault(); + e.preventDefault(); // get the mouse cursor position at startup: pos3 = e.clientX; pos4 = e.clientY; @@ -288,8 +266,6 @@ export class TooltipTextMenu { // stop moving when mouse button is released: document.onpointerup = null; document.onpointermove = null; - //self.highlightSearchTerms(self.state, ["hello"]); - //FormattedTextBox.Instance.unhighlightSearchTerms(); } } @@ -297,15 +273,33 @@ export class TooltipTextMenu { updateFontSizeDropdown(label: string) { //font SIZES const fontSizeBtns: MenuItem[] = []; - this.fontSizes.forEach(mark => { - fontSizeBtns.push(this.dropdownFontSizeBtn(String(mark.attrs.fontSize), "color: black; width: 50px;", mark, this.view, this.changeToFontSize)); - }); + const self = this; + this.fontSizes.forEach(mark => + fontSizeBtns.push(new MenuItem({ + title: "Set Font Size", + label: String(mark.attrs.fontSize), + execEvent: "", + class: "dropdown-item", + css: "color: black; width: 50px;", + enable() { return true; }, + run() { + const size = mark.attrs.fontSize; + if (size) { self.updateFontSizeDropdown(String(size) + " pt"); } + if (self.editorProps) { + const ruleProvider = self.editorProps.ruleProvider; + const heading = NumCast(self.editorProps.Document.heading); + if (ruleProvider && heading) { + ruleProvider["ruleSize_" + heading] = size; + } + } + TooltipTextMenu.setMark(self.view.state.schema.marks.pFontSize.create({ fontSize: size }), self.view.state, self.view.dispatch); + } + }))); - const newfontSizeDom = (new Dropdown(fontSizeBtns, { - label: label, - css: "color:black; min-width: 60px;" - }) as MenuItem).render(this.view).dom; - if (this.fontSizeDom) { this.tooltip.replaceChild(newfontSizeDom, this.fontSizeDom); } + const newfontSizeDom = (new Dropdown(fontSizeBtns, { label: label, css: "color:black; min-width: 60px;" }) as MenuItem).render(this.view).dom; + if (this.fontSizeDom) { + this.tooltip.replaceChild(newfontSizeDom, this.fontSizeDom); + } else { this.tooltip.appendChild(newfontSizeDom); } @@ -316,112 +310,38 @@ export class TooltipTextMenu { updateFontStyleDropdown(label: string) { //font STYLES const fontBtns: MenuItem[] = []; - 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)); - }); + const self = this; + this.fontStyles.forEach(mark => + fontBtns.push(new MenuItem({ + title: "Set Font Family", + label: mark.attrs.family, + execEvent: "", + class: "dropdown-item", + css: "color: black; font-family: " + mark.attrs.family + ", sans-serif; width: 125px;", + enable() { return true; }, + run() { + const fontName = mark.attrs.family; + if (fontName) { self.updateFontStyleDropdown(fontName); } + if (self.editorProps) { + const ruleProvider = self.editorProps.ruleProvider; + const heading = NumCast(self.editorProps.Document.heading); + if (ruleProvider && heading) { + ruleProvider["ruleFont_" + heading] = fontName; + } + } + TooltipTextMenu.setMark(self.view.state.schema.marks.pFontFamily.create({ family: fontName }), self.view.state, self.view.dispatch); + } + }))); - const newfontStyleDom = (new Dropdown(fontBtns, { - label: label, - css: "color:black; width: 125px;" - }) as MenuItem).render(this.view).dom; - if (this.fontStyleDom) { this.tooltip.replaceChild(newfontStyleDom, this.fontStyleDom); } + const newfontStyleDom = (new Dropdown(fontBtns, { label: label, css: "color:black; width: 125px;" }) as MenuItem).render(this.view).dom; + if (this.fontStyleDom) { + this.tooltip.replaceChild(newfontStyleDom, this.fontStyleDom); + } else { this.tooltip.appendChild(newfontStyleDom); } this.fontStyleDom = newfontStyleDom; } - - updateLinkMenu() { - if (!this.linkEditor || !this.linkText) { - this.linkEditor = document.createElement("div"); - this.linkEditor.className = "ProseMirror-icon menuicon"; - this.linkText = document.createElement("div"); - this.linkText.setAttribute("contenteditable", "true"); - this.linkText.style.whiteSpace = "nowrap"; - this.linkText.style.width = "150px"; - this.linkText.style.overflow = "hidden"; - this.linkText.style.color = "white"; - this.linkText.onpointerdown = (e: PointerEvent) => { e.stopPropagation(); }; - const linkBtn = document.createElement("div"); - linkBtn.textContent = ">>"; - linkBtn.style.width = "10px"; - linkBtn.style.height = "10px"; - linkBtn.style.color = "white"; - linkBtn.style.cssFloat = "left"; - linkBtn.onpointerdown = (e: PointerEvent) => { - const node = this.view.state.selection.$from.nodeAfter; - const link = node && node.marks.find(m => m.type.name === "link"); - if (link) { - const href: string = link.attrs.href; - if (href.indexOf(Utils.prepend("/doc/")) === 0) { - const docid = href.replace(Utils.prepend("/doc/"), ""); - DocServer.GetRefField(docid).then(action((f: Opt<Field>) => { - if (f instanceof Doc) { - if (DocumentManager.Instance.getDocumentView(f)) { - DocumentManager.Instance.getDocumentView(f)!.props.focus(f, false); - } - else this.editorProps && this.editorProps.addDocTab(f, undefined, "onRight"); - } - })); - } - // TODO This should have an else to handle external links - e.stopPropagation(); - e.preventDefault(); - } - }; - this.linkDrag = document.createElement("img"); - this.linkDrag.src = "https://seogurusnyc.com/wp-content/uploads/2016/12/link-1.png"; - this.linkDrag.style.width = "15px"; - this.linkDrag.style.height = "15px"; - this.linkDrag.title = "Drag to create link"; - this.linkDrag.id = "link-drag"; - this.linkDrag.onpointerdown = (e: PointerEvent) => { - if (!this.editorProps) return; - const dragData = new DragManager.LinkDragData(this.editorProps.Document); - dragData.dontClearTextBox = true; - // hack to get source context -sy - const docView = DocumentManager.Instance.getDocumentView(this.editorProps.Document); - e.stopPropagation(); - const ctrlKey = e.ctrlKey; - DragManager.StartLinkDrag(this.linkDrag!, dragData, e.clientX, e.clientY, - { - handlers: { - dragComplete: action(() => { - if (dragData.linkDocument) { - const linkDoc = dragData.linkDocument; - const proto = Doc.GetProto(linkDoc); - if (proto && docView) { - proto.sourceContext = docView.props.ContainingCollectionDoc; - } - const text = this.makeLink(linkDoc, StrCast(linkDoc.anchor2.title), ctrlKey ? "onRight" : "inTab"); - if (linkDoc instanceof Doc && linkDoc.anchor2 instanceof Doc) { - proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODODO open to more descriptive descriptions of following in text link - } - } - }), - }, - hideSource: false - }); - e.stopPropagation(); - e.preventDefault(); - }; - this.linkEditor.appendChild(this.linkDrag); - this.tooltip.appendChild(this.linkEditor); - } - - const node = this.view.state.selection.$from.nodeAfter; - const link = node && node.marks.find(m => m.type.name === "link"); - this.linkText.textContent = link ? link.attrs.href : "-empty-"; - - this.linkText.onkeydown = (e: KeyboardEvent) => { - if (e.key === "Enter") { - // this.makeLink(this.linkText!.textContent!); - e.stopPropagation(); - e.preventDefault(); - } - }; - } - async getTextLinkTargetTitle() { const node = this.view.state.selection.$from.nodeAfter; const link = node && node.marks.find(m => m.type.name === "link"); @@ -455,8 +375,21 @@ export class TooltipTextMenu { } } - async createLinkDropdown() { - const targetTitle = await this.getTextLinkTargetTitle(); + // LINK TOOL + createLinkTool(active: boolean = false) { + return new MenuItem({ + title: "Link tool", + label: "Link tool", + icon: icons.link, + css: "fill:white;", + class: active ? "menuicon-active" : "menuicon", + execEvent: "", + run: async (state, dispatch) => { }, + active: (state) => true + }); + } + + createLinkDropdown(targetTitle: string) { const input = document.createElement("input"); // menu item for input for hyperlink url @@ -484,9 +417,7 @@ export class TooltipTextMenu { return div; }, enable() { return false; }, - run(p1, p2, p3, event) { - event.stopPropagation(); - } + run(p1, p2, p3, event) { event.stopPropagation(); } }); // menu item to update/apply the hyperlink to the selected text @@ -502,9 +433,22 @@ export class TooltipTextMenu { return button; }, enable() { return false; }, - run: (state, dispatch, view, event) => { + run: async (state, dispatch, view, event) => { event.stopPropagation(); - this.makeLinkToURL(input.value, "onRight"); + let node = this.view.state.selection.$from.nodeAfter; + let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: input.value, location: "onRight" }); + 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"); + + // update link menu + const linkDom = self.createLinkTool(true).render(self.view).dom; + const linkDropdownDom = self.createLinkDropdown(await self.getTextLinkTargetTitle()).render(self.view).dom; + self.linkDom && self.tooltip.replaceChild(linkDom, self.linkDom); + self.linkDropdownDom && self.tooltip.replaceChild(linkDropdownDom, self.linkDropdownDom); + self.linkDom = linkDom; + self.linkDropdownDom = linkDropdownDom; } }); @@ -526,190 +470,55 @@ export class TooltipTextMenu { }, enable() { return true; }, async run() { - self.deleteLink(); - // update link dropdown - const dropdown = await self.createLinkDropdown(); - const newLinkDropdowndom = dropdown.render(self.view).dom; - self._linkDropdownDom && self.tooltip.replaceChild(newLinkDropdowndom, self._linkDropdownDom); - self._linkDropdownDom = newLinkDropdowndom; - } - }); - - - const linkDropdown = new Dropdown(targetTitle ? [linkInfo, linkApply, deleteLink] : [linkInfo, linkApply], { class: "buttonSettings-dropdown" }) as MenuItem; - return linkDropdown; - } - - // makeLinkWithState = (state: EditorState, target: string, location: string) => { - // let link = state.schema.mark(state.schema.marks.link, { href: target, location: location }); - // } - - makeLink = (targetDoc: Doc, title: string, location: string): string => { - const link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + targetDoc[Id]), title: title, 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). - addMark(this.view.state.selection.from, this.view.state.selection.to, link)); - const node = this.view.state.selection.$from.nodeAfter; - if (node && node.text) { - return node.text; - } - return ""; - } - - makeLinkToURL = (target: String, lcoation: 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 }); - 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"); - } - - deleteLink = () => { - const node = this.view.state.selection.$from.nodeAfter; - const link = node && node.marks.find(m => m.type === this.view.state.schema.marks.link); - const href = link!.attrs.href; - if (href) { - if (href.indexOf(Utils.prepend("/doc/")) === 0) { - const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0]; - if (linkclicked) { - DocServer.GetRefField(linkclicked).then(async linkDoc => { + // delete the link + const node = self.view.state.selection.$from.nodeAfter; + const link = node && node.marks.find(m => m.type === self.view.state.schema.marks.link); + const href = link!.attrs.href; + if (href ?.indexOf(Utils.prepend("/doc/")) === 0) { + const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0]; + linkclicked && DocServer.GetRefField(linkclicked).then(async linkDoc => { if (linkDoc instanceof Doc) { LinkManager.Instance.deleteLink(linkDoc); - this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link)); + self.view.dispatch(self.view.state.tr.removeMark(self.view.state.selection.from, self.view.state.selection.to, self.view.state.schema.marks.link)); } }); } - } - } - } - - deleteLinkItem() { - const icon = { - height: 16, width: 16, - path: "M15.898,4.045c-0.271-0.272-0.713-0.272-0.986,0l-4.71,4.711L5.493,4.045c-0.272-0.272-0.714-0.272-0.986,0s-0.272,0.714,0,0.986l4.709,4.711l-4.71,4.711c-0.272,0.271-0.272,0.713,0,0.986c0.136,0.136,0.314,0.203,0.492,0.203c0.179,0,0.357-0.067,0.493-0.203l4.711-4.711l4.71,4.711c0.137,0.136,0.314,0.203,0.494,0.203c0.178,0,0.355-0.067,0.492-0.203c0.273-0.273,0.273-0.715,0-0.986l-4.711-4.711l4.711-4.711C16.172,4.759,16.172,4.317,15.898,4.045z" - }; - return new MenuItem({ - title: "Delete Link", - label: "X", - icon: icon, - css: "color: red", - class: "summarize", - execEvent: "", - run: (state, dispatch) => { - this.deleteLink(); + // update link menu + const linkDom = self.createLinkTool(false).render(self.view).dom; + const linkDropdownDom = self.createLinkDropdown("").render(self.view).dom; + self.linkDom && self.tooltip.replaceChild(linkDom, self.linkDom); + self.linkDropdownDom && self.tooltip.replaceChild(linkDropdownDom, self.linkDropdownDom); + self.linkDom = linkDom; + self.linkDropdownDom = linkDropdownDom; } }); - } - createLink() { - const markType = schema.marks.link; - return new MenuItem({ - title: "Add or remove link", - label: "Add or remove link", - execEvent: "", - icon: icons.link, - css: "color:white;", - class: "menuicon", - enable(state) { return !state.selection.empty; }, - run: (state, dispatch, view) => { - // to remove link - let curLink = ""; - if (this.markActive(state, markType)) { - - const { from, $from, to, empty } = state.selection; - const node = state.doc.nodeAt(from); - node && node.marks.map(m => { - m.type === markType && (curLink = m.attrs.href); - }); - //toggleMark(markType)(state, dispatch); - //return true; - } - // to create link - openPrompt({ - title: "Create a link", - fields: { - href: new TextField({ - value: curLink, - label: "Link Target", - required: true - }), - title: new TextField({ label: "Title" }) - }, - callback(attrs: any) { - toggleMark(markType, attrs)(view.state, view.dispatch); - view.focus(); - }, - flyout_top: 0, - flyout_left: 0 - }); - } - }); + return new Dropdown(targetTitle ? [linkInfo, linkApply, deleteLink] : [linkInfo, linkApply], { class: "buttonSettings-dropdown" }) as MenuItem; } - //will display a remove-list-type button if selection is in list, otherwise will show list type dropdown - updateListItemDropdown(label: string, listTypeBtn: any) { - //remove old btn - if (listTypeBtn) { this.tooltip.removeChild(listTypeBtn); } - - //Make a dropdown of all list types - const toAdd: MenuItem[] = []; - this.listTypeToIcon.forEach((icon, type) => { - toAdd.push(this.dropdownNodeBtn(icon, "color: black; width: 40px;", type, this.view, this.listTypes, this.changeToNodeType)); - }); - //option to remove the list formatting - toAdd.push(this.dropdownNodeBtn("X", "color: black; width: 40px;", undefined, this.view, this.listTypes, this.changeToNodeType)); - - listTypeBtn = (new Dropdown(toAdd, { - label: label, - css: "color:black; width: 40px;" - }) as MenuItem).render(this.view).dom; - - //add this new button and return it - this.tooltip.appendChild(listTypeBtn); - return listTypeBtn; - } - - createStar() { - return new MenuItem({ - title: "Summarize", - label: "Summarize", - icon: icons.join, - css: "color:white;", - class: "menuicon", - execEvent: "", - run: (state, dispatch) => { - TooltipTextMenu.insertStar(this.view.state, this.view.dispatch); - } - - }); - } - - public static insertStar(state: EditorState<any>, dispatch: any) { - if (state.selection.empty) return false; - const mark = state.schema.marks.highlight.create(); - const tr = state.tr; - tr.addMark(state.selection.from, state.selection.to, mark); - const content = tr.selection.content(); - const newNode = state.schema.nodes.star.create({ visibility: false, text: content, textslice: content.toJSON() }); - dispatch && dispatch(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark)); - return true; + public MakeLinkToSelection = (linkDocId: string, title: string, location: string, targetDocId: string): string => { + const link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + linkDocId), title: title, location: location, targetId: targetDocId }); + this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link). + addMark(this.view.state.selection.from, this.view.state.selection.to, link)); + return this.view.state.selection.$from.nodeAfter ?.text || ""; } - public static insertComment(state: EditorState<any>, dispatch: any) { - if (state.selection.empty) return false; - const mark = state.schema.marks.highlight.create(); - const tr = state.tr; - tr.addMark(state.selection.from, state.selection.to, mark); - const content = tr.selection.content(); - const newNode = state.schema.nodes.star.create({ visibility: false, text: content, textslice: content.toJSON() }); - dispatch && dispatch(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark)); - return true; + // SUMMARIZER TOOL + static insertSummarizer(state: EditorState<any>, dispatch: any) { + if (!state.selection.empty) { + const mark = state.schema.marks.summarize.create(); + const tr = state.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 ?.(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark)); + } } + // HIGHLIGHTER TOOL createHighlightTool() { return new MenuItem({ title: "Highlight", - css: "color:white;", + css: "fill:white;", class: "menuicon", execEvent: "", render() { @@ -719,27 +528,22 @@ export class TooltipTextMenu { path.setAttributeNS(null, "d", "M0 479.98L99.92 512l35.45-35.45-67.04-67.04L0 479.98zm124.61-240.01a36.592 36.592 0 0 0-10.79 38.1l13.05 42.83-50.93 50.94 96.23 96.23 50.86-50.86 42.74 13.08c13.73 4.2 28.65-.01 38.15-10.78l35.55-41.64-173.34-173.34-41.52 35.44zm403.31-160.7l-63.2-63.2c-20.49-20.49-53.38-21.52-75.12-2.35L190.55 183.68l169.77 169.78L530.27 154.4c19.18-21.74 18.15-54.63-2.35-75.13z"); svg.appendChild(path); - const color = document.createElement("div"); - color.className = "buttonColor"; - color.style.backgroundColor = TooltipTextMenuManager.Instance.highlight.toString(); + const color = TooltipTextMenu.createDiv("buttonColor"); + color.style.backgroundColor = TooltipTextMenuManager.Instance.highlighter.toString(); - const wrapper = document.createElement("div"); - wrapper.id = "colorPicker"; + const wrapper = TooltipTextMenu.createDiv("colorPicker"); wrapper.appendChild(svg); wrapper.appendChild(color); return wrapper; }, - run: (state, dispatch) => { - TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlight, this.view.state, this.view.dispatch); - } + run: (state, dispatch) => TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlighter, state, dispatch) }); } - public static insertHighlight(color: String, state: EditorState<any>, dispatch: any) { - if (state.selection.empty) return false; - - const highlightMark = state.schema.mark(state.schema.marks.marker, { highlight: color }); - dispatch(state.tr.addMark(state.selection.from, state.selection.to, highlightMark)); + static insertHighlight(color: String, state: EditorState<any>, dispatch: any) { + if (!state.selection.empty) { + toggleMark(state.schema.marks.marker, { highlight: color })(state, dispatch); + } } createHighlightDropdown() { @@ -754,8 +558,7 @@ export class TooltipTextMenu { const p = document.createElement("p"); p.textContent = "Change highlight:"; - const colorsWrapper = document.createElement("div"); - colorsWrapper.className = "colorPicker-wrapper"; + const colorsWrapper = TooltipTextMenu.createDiv("colorPicker-wrapper"); const colors = [ PastelSchemaPalette.get("pink2"), @@ -772,22 +575,22 @@ export class TooltipTextMenu { colors.forEach(color => { const button = document.createElement("button"); - button.className = color === TooltipTextMenuManager.Instance.highlight ? "colorPicker active" : "colorPicker"; + button.className = color === TooltipTextMenuManager.Instance.highlighter ? "colorPicker active" : "colorPicker"; if (color) { button.style.backgroundColor = color; button.textContent = color === "transparent" ? "X" : ""; button.onclick = e => { - TooltipTextMenuManager.Instance.highlight = color; + TooltipTextMenuManager.Instance.highlighter = color; - TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlight, self.view.state, self.view.dispatch); + TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlighter, self.view.state, self.view.dispatch); // update color menu const highlightDom = self.createHighlightTool().render(self.view).dom; const highlightDropdownDom = self.createHighlightDropdown().render(self.view).dom; - self.highlightDom && self.tooltip.replaceChild(highlightDom, self.highlightDom); - self.highlightDropdownDom && self.tooltip.replaceChild(highlightDropdownDom, self.highlightDropdownDom); - self.highlightDom = highlightDom; - self.highlightDropdownDom = highlightDropdownDom; + self.highighterDom && self.tooltip.replaceChild(highlightDom, self.highighterDom); + self.highlighterDropdownDom && self.tooltip.replaceChild(highlightDropdownDom, self.highlighterDropdownDom); + self.highighterDom = highlightDom; + self.highlighterDropdownDom = highlightDropdownDom; }; } colorsWrapper.appendChild(button); @@ -804,14 +607,14 @@ export class TooltipTextMenu { } }); - const colorDropdown = new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem; - return colorDropdown; + return new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem; } + // COLOR TOOL createColorTool() { return new MenuItem({ title: "Color", - css: "color:white;", + css: "fill:white;", class: "menuicon", execEvent: "", render() { @@ -821,27 +624,25 @@ export class TooltipTextMenu { path.setAttributeNS(null, "d", "M204.3 5C104.9 24.4 24.8 104.3 5.2 203.4c-37 187 131.7 326.4 258.8 306.7 41.2-6.4 61.4-54.6 42.5-91.7-23.1-45.4 9.9-98.4 60.9-98.4h79.7c35.8 0 64.8-29.6 64.9-65.3C511.5 97.1 368.1-26.9 204.3 5zM96 320c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm32-128c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128-64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128 64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z"); svg.appendChild(path); - const color = document.createElement("div"); - color.className = "buttonColor"; + const color = TooltipTextMenu.createDiv("buttonColor"); color.style.backgroundColor = TooltipTextMenuManager.Instance.color.toString(); - const wrapper = document.createElement("div"); - wrapper.id = "colorPicker"; + const wrapper = TooltipTextMenu.createDiv("colorPicker"); wrapper.appendChild(svg); wrapper.appendChild(color); return wrapper; }, - run: (state, dispatch) => { - TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, this.view.state, this.view.dispatch); - } + run: (state, dispatch) => TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, state, dispatch) }); } - public static insertColor(color: String, state: EditorState<any>, dispatch: any) { - if (state.selection.empty) return false; - - const colorMark = state.schema.mark(state.schema.marks.color, { color: color }); - dispatch(state.tr.addMark(state.selection.from, state.selection.to, colorMark)); + static insertColor(color: String, state: EditorState<any>, dispatch: any) { + const colorMark = state.schema.mark(state.schema.marks.pFontColor, { color: color }); + if (state.selection.empty) { + dispatch(state.tr.addStoredMark(colorMark)); + } else { + this.setMark(colorMark, state, dispatch); + } } createColorDropdown() { @@ -856,8 +657,7 @@ export class TooltipTextMenu { const p = document.createElement("p"); p.textContent = "Change color:"; - const colorsWrapper = document.createElement("div"); - colorsWrapper.className = "colorPicker-wrapper"; + const colorsWrapper = TooltipTextMenu.createDiv("colorPicker-wrapper"); const colors = [ DarkPastelSchemaPalette.get("pink2"), @@ -900,16 +700,14 @@ export class TooltipTextMenu { return div; }, enable() { return false; }, - run(p1, p2, p3, event) { - event.stopPropagation(); - } + run(p1, p2, p3, event) { event.stopPropagation(); } }); - const colorDropdown = new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem; - return colorDropdown; + return new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem; } - createBrush(active: boolean = false) { + // BRUSH TOOL + createBrushTool(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" @@ -919,7 +717,7 @@ export class TooltipTextMenu { title: "Brush tool", label: "Brush tool", icon: icon, - css: "color:white;", + css: "fill:white;", class: active ? "menuicon-active" : "menuicon", execEvent: "", run: (state, dispatch) => { @@ -930,23 +728,23 @@ export class TooltipTextMenu { self._brushDropdownDom && self.tooltip.replaceChild(newBrushDropdowndom, self._brushDropdownDom); self._brushDropdownDom = newBrushDropdowndom; }, - active: (state) => { - return true; - } + active: (state) => true }); } brush_function(state: EditorState<any>, dispatch: any) { if (TooltipTextMenuManager.Instance._brushIsEmpty) { - const selected_marks = this.getMarksInSelection(this.view.state); - if (this._brushdom) { - if (selected_marks.size >= 0) { - TooltipTextMenuManager.Instance._brushMarks = selected_marks; - const newbrush = this.createBrush(true).render(this.view).dom; - this.tooltip.replaceChild(newbrush, this._brushdom); - this._brushdom = newbrush; - TooltipTextMenuManager.Instance._brushIsEmpty = !TooltipTextMenuManager.Instance._brushIsEmpty; - } + // get marks in the selection + const selected_marks = new Set<Mark>(); + const { from, to } = state.selection as TextSelection; + state.doc.nodesBetween(from, to, (node) => node.marks ?.forEach(m => selected_marks.add(m))); + + if (this._brushdom && selected_marks.size >= 0) { + TooltipTextMenuManager.Instance._brushMarks = selected_marks; + const newbrush = this.createBrushTool(true).render(this.view).dom; + this.tooltip.replaceChild(newbrush, this._brushdom); + this._brushdom = newbrush; + TooltipTextMenuManager.Instance._brushIsEmpty = !TooltipTextMenuManager.Instance._brushIsEmpty; } } else { @@ -956,13 +754,12 @@ export class TooltipTextMenu { if (TooltipTextMenuManager.Instance._brushMarks && to - from > 0) { this.view.dispatch(this.view.state.tr.removeMark(from, to)); Array.from(TooltipTextMenuManager.Instance._brushMarks).filter(m => m.type !== schema.marks.user_mark).forEach((mark: Mark) => { - const markType = mark.type; - this.changeToMarkInGroup(markType, this.view, []); + TooltipTextMenu.setMark(mark, this.view.state, this.view.dispatch); }); } } else { - const newbrush = this.createBrush(false).render(this.view).dom; + const newbrush = this.createBrushTool(false).render(this.view).dom; this.tooltip.replaceChild(newbrush, this._brushdom); this._brushdom = newbrush; TooltipTextMenuManager.Instance._brushIsEmpty = !TooltipTextMenuManager.Instance._brushIsEmpty; @@ -974,17 +771,12 @@ export class TooltipTextMenu { createBrushDropdown(active: boolean = false) { let label = "Stored marks: "; if (TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMarks.size > 0) { - TooltipTextMenuManager.Instance._brushMarks.forEach((mark: Mark) => { - const markType = mark.type; - label += markType.name; - label += ", "; - }); + TooltipTextMenuManager.Instance._brushMarks.forEach((mark: Mark) => label += mark.type.name + ", "); label = label.substring(0, label.length - 2); } else { label = "No marks are currently stored"; } - const brushInfo = new MenuItem({ title: "", label: label, @@ -992,12 +784,11 @@ export class TooltipTextMenu { class: "button-setting-disabled", css: "", enable() { return false; }, - run(p1, p2, p3, event) { - event.stopPropagation(); - } + run(p1, p2, p3, event) { event.stopPropagation(); } }); const self = this; + const input = document.createElement("input"); const clearBrush = new MenuItem({ title: "Clear brush", execEvent: "", @@ -1007,7 +798,31 @@ export class TooltipTextMenu { const button = document.createElement("button"); button.textContent = "Clear brush"; + input.textContent = "editme"; + input.style.width = "75px"; + input.style.height = "30px"; + input.style.background = "white"; + input.setAttribute("contenteditable", "true"); + input.style.whiteSpace = "nowrap"; + input.type = "text"; + input.placeholder = "Enter URL"; + input.onpointerdown = (e: PointerEvent) => { + e.stopPropagation(); + e.preventDefault(); + }; + input.onclick = (e: MouseEvent) => { + input.select(); + input.focus(); + }; + input.onkeypress = (e: KeyboardEvent) => { + if (e.key === "Enter") { + TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMap.set(input.value, TooltipTextMenuManager.Instance._brushMarks); + input.style.background = "lightGray"; + } + }; + const wrapper = document.createElement("div"); + wrapper.appendChild(input); wrapper.appendChild(button); return wrapper; }, @@ -1018,7 +833,7 @@ export class TooltipTextMenu { // update brush tool // TODO: this probably isn't very clean - const newBrushdom = self.createBrush().render(self.view).dom; + const newBrushdom = self.createBrushTool().render(self.view).dom; self._brushdom && self.tooltip.replaceChild(newBrushdom, self._brushdom); self._brushdom = newBrushdom; const newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom; @@ -1028,295 +843,31 @@ export class TooltipTextMenu { }); const hasMarks = TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMarks.size > 0; - const brushDom = new Dropdown(hasMarks ? [brushInfo, clearBrush] : [brushInfo], { class: "buttonSettings-dropdown" }) as MenuItem; - return brushDom; + return new Dropdown(hasMarks ? [brushInfo, clearBrush] : [brushInfo], { class: "buttonSettings-dropdown" }) as MenuItem; } - //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[]) => { - const { $cursor, ranges } = view.state.selection as TextSelection; - const state = view.state; - const dispatch = view.dispatch; - - //remove all other active font marks - fontMarks.forEach((type) => { - if (dispatch) { - if ($cursor) { - if (type.isInSet(state.storedMarks || $cursor.marks())) { - dispatch(state.tr.removeStoredMark(type)); - } - } else { - let has = false; - for (let i = 0; !has && i < ranges.length; i++) { - const { $from, $to } = ranges[i]; - has = state.doc.rangeHasMark($from.pos, $to.pos, type); - } - for (const i of ranges) { - if (has) { - toggleMark(type)(view.state, view.dispatch, view); - } - } - } - } - }); - - if (markType) { - //actually apply font - if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) { - const 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: markType.name, setFontSize: Number(markType.name.replace(/p/, "")) }), view.state.schema); - view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from)))); - } - else toggleMark(markType)(view.state, view.dispatch, view); - } - } - - changeToFontFamily = (mark: Mark, view: EditorView) => { - const { $cursor, ranges } = view.state.selection as TextSelection; - const state = view.state; - const 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++) { - const { $from, $to } = ranges[i]; - has = state.doc.rangeHasMark($from.pos, $to.pos, view.state.schema.marks.pFontFamily); - } - for (const i of ranges) { - if (has) { - toggleMark(view.state.schema.marks.pFontFamily)(view.state, view.dispatch, view); - } - } - } - const fontName = mark.attrs.family; - if (fontName) { this.updateFontStyleDropdown(fontName); } - if (this.editorProps) { - const ruleProvider = this.editorProps.ruleProvider; - const 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) { - const 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) => { - const { $cursor, ranges } = view.state.selection as TextSelection; - const state = view.state; - const 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++) { - const { $from, $to } = ranges[i]; - has = state.doc.rangeHasMark($from.pos, $to.pos, view.state.schema.marks.pFontSize); - } - for (const i of ranges) { - if (has) { - toggleMark(view.state.schema.marks.pFontSize)(view.state, view.dispatch, view); - } - } - } - - const size = mark.attrs.fontSize; - if (size) { this.updateFontSizeDropdown(String(size) + " pt"); } - if (this.editorProps) { - const ruleProvider = this.editorProps.ruleProvider; - const 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) { - const 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 - const view = this.view; - if (nodeType === schema.nodes.bullet_list) { - wrapInList(nodeType)(view.state, view.dispatch); - } else { - const marks = view.state.storedMarks || (view.state.selection.$to.parentOffset && view.state.selection.$from.marks()); - if (!wrapInList(schema.nodes.ordered_list)(view.state, (tx2: any) => { - const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle); - marks && tx3.ensureMarks([...marks]); - marks && tx3.setStoredMarks([...marks]); - - view.dispatch(tx2); - })) { - const tx2 = view.state.tr; - const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle); - marks && tx3.ensureMarks([...marks]); - marks && tx3.setStoredMarks([...marks]); - - view.dispatch(tx3); - } - } - } - - //makes a button for the drop down FOR MARKS - //css is the style you want applied to the button - dropdownFontFamilyBtn(label: string, css: string, mark: Mark, view: EditorView, changeFontFamily: (mark: Mark<any>, view: EditorView) => any) { - return new MenuItem({ - title: "", - label: label, - execEvent: "", - class: "dropdown-item", - css: css, - enable() { return true; }, - run() { - 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: "dropdown-item", - css: css, - enable() { return true; }, - run() { - changeFontSize(mark, view); - } - }); - } - - //makes a button for the drop down FOR NODE TYPES - //css is the style you want applied to the button - dropdownNodeBtn(label: string, css: string, nodeType: NodeType | undefined, view: EditorView, groupNodes: NodeType[], changeToNodeInGroup: (nodeType: NodeType<any> | undefined, view: EditorView, groupNodes: NodeType[]) => any) { - return new MenuItem({ - title: "", - label: label, - execEvent: "", - class: "dropdown-item", - css: css, - enable() { return true; }, - run() { - changeToNodeInGroup(nodeType, view, groupNodes); - } - }); - } - - markActive = function (state: EditorState<any>, type: MarkType<Schema<string, string>>) { - const { from, $from, to, empty } = state.selection; - if (empty) return type.isInSet(state.storedMarks || $from.marks()); - else return state.doc.rangeHasMark(from, to, type); - }; - - // Helper function to create menu icons - icon(text: string, name: string, title: string = name) { - const span = document.createElement("span"); - span.className = name + " menuicon"; - span.title = title; - span.textContent = text; - span.style.color = "white"; - return span; - } - - svgIcon(name: string, title: string = name, dpath: string) { - const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - svg.setAttribute("viewBox", "-100 -100 650 650"); - const path = document.createElementNS('http://www.w3.org/2000/svg', "path"); - path.setAttributeNS(null, "d", dpath); - svg.appendChild(path); - - const span = document.createElement("span"); - span.className = name + " menuicon"; - span.title = title; - span.appendChild(svg); - - return span; - } - - //method for checking whether node can be inserted - canInsert(state: EditorState, nodeType: NodeType<Schema<string, string>>) { - const $from = state.selection.$from; - for (let d = $from.depth; d >= 0; d--) { - const index = $from.index(d); - if ($from.node(d).canReplaceWith(index, index, nodeType)) return true; - } - return false; - } - - - //adapted this method - use it to check if block has a tag (ie bulleting) - blockActive(type: NodeType<Schema<string, string>>, state: EditorState) { - const attrs = {}; - - if (state.selection instanceof NodeSelection) { - const sel: NodeSelection = state.selection; - const $from = sel.$from; - const to = sel.to; - const node = sel.node; - - if (node) { - return node.hasMarkup(type, attrs); - } - - return to <= $from.end() && $from.parent.hasMarkup(type, attrs); - } - } - - // Create an icon for a heading at the given level - heading(level: number) { - return { - command: setBlockType(schema.nodes.heading, { level }), - dom: this.icon("H" + level, "heading") - }; - } - - getMarksInSelection(state: EditorState<any>) { - const found = new Set<Mark>(); - const { from, to } = state.selection as TextSelection; - state.doc.nodesBetween(from, to, (node) => { - const marks = node.marks; - if (marks) { - marks.forEach(m => { - found.add(m); + static setMark = (mark: Mark, state: EditorState<any>, dispatch: any) => { + if (mark) { + 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 }; + 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 { + 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); }); } - }); - return found; - } - - reset_mark_doms() { - const iterator = this._marksToDoms.values(); - let next = iterator.next(); - while (!next.done) { - next.value.style.color = "white"; - next = iterator.next(); } } + // called by Prosemirror update(view: EditorView, lastState: EditorState | undefined) { this.updateFromDash(view, lastState, this.editorProps); } //updates the tooltip menu when the selection changes public async updateFromDash(view: EditorView, lastState: EditorState | undefined, props: any) { @@ -1325,64 +876,45 @@ export class TooltipTextMenu { return; } this.view = view; - const state = view.state; DocumentDecorations.Instance.showTextBar(); 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; - - this.reset_mark_doms(); - - // Hide the tooltip if the selection is empty - if (state.selection.empty) { - //this.tooltip.style.display = "none"; - //return; - } - // update link dropdown - const linkDropdown = await this.createLinkDropdown(); - const newLinkDropdowndom = linkDropdown.render(this.view).dom; - this._linkDropdownDom && this.tooltip.replaceChild(newLinkDropdowndom, this._linkDropdownDom); - this._linkDropdownDom = newLinkDropdowndom; - - //UPDATE FONT STYLE DROPDOWN - const activeStyles = this.activeFontFamilyOnSelection(); - if (activeStyles !== undefined) { - if (activeStyles.length === 1) { - console.log("updating font style dropdown", activeStyles[0]); - activeStyles[0] && this.updateFontStyleDropdown(activeStyles[0]); - } else this.updateFontStyleDropdown(activeStyles.length ? "various" : "default"); - } - - //UPDATE FONT SIZE DROPDOWN - const activeSizes = this.activeFontSizeOnSelection(); - if (activeSizes !== undefined) { - if (activeSizes.length === 1) { //if there's only one active font size - activeSizes[0] && this.updateFontSizeDropdown(String(activeSizes[0]) + " pt"); - } else this.updateFontSizeDropdown(activeSizes.length ? "various" : "default"); + // Don't do anything if the document/selection didn't change + if (!lastState || !lastState.doc.eq(view.state.doc) || !lastState.selection.eq(view.state.selection)) { + + // UPDATE LINK DROPDOWN + const linkTarget = await this.getTextLinkTargetTitle(); + const linkDom = this.createLinkTool(linkTarget ? true : false).render(this.view).dom; + const linkDropdownDom = this.createLinkDropdown(linkTarget).render(this.view).dom; + this.linkDom && this.tooltip.replaceChild(linkDom, this.linkDom); + this.linkDropdownDom && this.tooltip.replaceChild(linkDropdownDom, this.linkDropdownDom); + this.linkDom = linkDom; + this.linkDropdownDom = linkDropdownDom; + + //UPDATE FONT STYLE DROPDOWN + const activeStyles = this.activeFontFamilyOnSelection(); + this.updateFontStyleDropdown(activeStyles.length === 1 ? activeStyles[0] : activeStyles.length ? "various" : "default"); + + //UPDATE FONT SIZE DROPDOWN + const activeSizes = this.activeFontSizeOnSelection(); + this.updateFontSizeDropdown(activeSizes.length === 1 ? String(activeSizes[0]) + " pt" : activeSizes.length ? "various" : "default"); + + //UPDATE ALL OTHER BUTTONS + this.updateHighlightStateOfButtons(); } - - this.update_mark_doms(); } - update_mark_doms() { - this.reset_mark_doms(); - this._activeMarks.forEach((mark) => { - if (this._marksToDoms.has(mark)) { - const dom = this._marksToDoms.get(mark); - if (dom) dom.style.color = "greenyellow"; - } - }); + + updateHighlightStateOfButtons() { + Array.from(this._marksToDoms.values()).forEach(val => val.style.fill = "white"); + this.activeMarksOnSelection().filter(mark => this._marksToDoms.has(mark)).forEach(mark => + this._marksToDoms.get(mark)!.style.fill = "greenyellow"); // keeps brush tool highlighted if active when switching between textboxes - if (!TooltipTextMenuManager.Instance._brushIsEmpty) { - if (this._brushdom) { - const newbrush = this.createBrush(true).render(this.view).dom; - this.tooltip.replaceChild(newbrush, this._brushdom); - this._brushdom = newbrush; - } + if (!TooltipTextMenuManager.Instance._brushIsEmpty && this._brushdom) { + const newbrush = this.createBrushTool(true).render(this.view).dom; + this.tooltip.replaceChild(newbrush, this._brushdom); + this._brushdom = newbrush; } - } //finds fontSize at start of selection @@ -1410,24 +942,21 @@ export class TooltipTextMenu { return activeFamilies; } //finds all active marks on selection in given group - activeMarksOnSelection(markGroup: MarkType[]) { + activeMarksOnSelection() { + const markGroup = Array.from(this._marksToDoms.keys()); + if (this.view.state.storedMarks) return this.view.state.storedMarks.map(mark => mark.type); //current selection const { empty, ranges, $to } = this.view.state.selection as TextSelection; const state = this.view.state; - const dispatch = this.view.dispatch; - let activeMarks: MarkType[]; + let activeMarks: MarkType[] = []; if (!empty) { activeMarks = markGroup.filter(mark => { const has = false; for (let i = 0; !has && i < ranges.length; i++) { - const { $from, $to } = ranges[i]; - return state.doc.rangeHasMark($from.pos, $to.pos, mark); + return state.doc.rangeHasMark(ranges[i].$from.pos, ranges[i].$to.pos, mark); } return false; }); - - const refnode = this.reference_node($to); - this._activeMarks = refnode.marks; } else { const pos = this.view.state.selection.$from; @@ -1438,20 +967,14 @@ export class TooltipTextMenu { else { return []; } - this._activeMarks = ref_node.marks; activeMarks = markGroup.filter(mark_type => { if (mark_type === state.schema.marks.pFontSize) { return ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name); } const mark = state.schema.mark(mark_type); return ref_node.marks.includes(mark); - return false; }); } - else { - return []; - } - } return activeMarks; } @@ -1488,20 +1011,21 @@ export class TooltipTextMenu { } -class TooltipTextMenuManager { +export class TooltipTextMenuManager { private static _instance: TooltipTextMenuManager; + private _isPinned: boolean = false; public pinnedX: number = 0; public pinnedY: number = 0; public unpinnedX: number = 0; public unpinnedY: number = 0; - private _isPinned: boolean = false; public _brushMarks: Set<Mark> | undefined; + public _brushMap: Map<string, Set<Mark>> = new Map(); public _brushIsEmpty: boolean = true; public color: String = "#000"; - public highlight: String = "transparent"; + public highlighter: String = "transparent"; public activeMenu: TooltipTextMenu | undefined; @@ -1512,11 +1036,7 @@ class TooltipTextMenuManager { return TooltipTextMenuManager._instance; } - public get isPinned() { - return this._isPinned; - } + public get isPinned() { return this._isPinned; } - public toggleIsPinned() { - this._isPinned = !this._isPinned; - } + public toggleIsPinned() { this._isPinned = !this._isPinned; } } |