aboutsummaryrefslogtreecommitdiff
path: root/src/client/util/TooltipTextMenu.tsx
diff options
context:
space:
mode:
authorStanley Yip <stanley_yip@brown.edu>2020-01-08 13:47:29 -0500
committerStanley Yip <stanley_yip@brown.edu>2020-01-08 13:47:29 -0500
commitabfa42b6f2cf863deee19aac19328a23687464cb (patch)
treeb481f23ffa7bccbde7a31de34f50d765b6b73162 /src/client/util/TooltipTextMenu.tsx
parentd8fc218f3481728f221ceacc60ac4bc553f8e295 (diff)
parent19a71cb2788b9c1c8d8ced4af285bf91033ba626 (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into pen
Diffstat (limited to 'src/client/util/TooltipTextMenu.tsx')
-rw-r--r--src/client/util/TooltipTextMenu.tsx1335
1 files changed, 429 insertions, 906 deletions
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 5136089b3..8aa304fad 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, Transaction } from "prosemirror-state";
+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
- let 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,15 @@ export class TooltipTextMenu {
this.tooltip.appendChild(this.colorDropdownDom);
// link menu
- this.updateLinkMenu();
- let 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 }));
this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 10 }));
this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 12 }));
this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 14 }));
@@ -194,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() {
- let draggerWrapper = document.createElement("div");
- draggerWrapper.className = "dragger-wrapper";
- let 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);
- let line1 = document.createElement("span");
- line1.className = "dragger-line";
- let line2 = document.createElement("span");
- line2.className = "dragger-line";
- let 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;
@@ -285,24 +266,40 @@ export class TooltipTextMenu {
// 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
updateFontSizeDropdown(label: string) {
//font SIZES
- let fontSizeBtns: MenuItem[] = [];
- this.fontSizes.forEach(mark => {
- fontSizeBtns.push(this.dropdownFontSizeBtn(String(mark.attrs.fontSize), "color: black; width: 50px;", mark, this.view, this.changeToFontSize));
- });
+ const fontSizeBtns: MenuItem[] = [];
+ 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);
+ }
+ })));
- let 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);
}
@@ -312,127 +309,53 @@ export class TooltipTextMenu {
//label of dropdown will change to given label
updateFontStyleDropdown(label: string) {
//font STYLES
- let 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 fontBtns: MenuItem[] = [];
+ 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);
+ }
+ })));
- let 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(); };
- let 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) => {
- let node = this.view.state.selection.$from.nodeAfter;
- let link = node && node.marks.find(m => m.type.name === "link");
- if (link) {
- let href: string = link.attrs.href;
- if (href.indexOf(Utils.prepend("/doc/")) === 0) {
- let 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;
- let dragData = new DragManager.LinkDragData(this.editorProps.Document);
- dragData.dontClearTextBox = true;
- // hack to get source context -sy
- let docView = DocumentManager.Instance.getDocumentView(this.editorProps.Document);
- e.stopPropagation();
- let ctrlKey = e.ctrlKey;
- DragManager.StartLinkDrag(this.linkDrag!, dragData, e.clientX, e.clientY,
- {
- handlers: {
- dragComplete: action(() => {
- if (dragData.linkDocument) {
- let linkDoc = dragData.linkDocument;
- let proto = Doc.GetProto(linkDoc);
- if (proto && docView) {
- proto.sourceContext = docView.props.ContainingCollectionDoc;
- }
- let 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);
- }
-
- let node = this.view.state.selection.$from.nodeAfter;
- let 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() {
- let node = this.view.state.selection.$from.nodeAfter;
- let link = node && node.marks.find(m => m.type.name === "link");
+ const node = this.view.state.selection.$from.nodeAfter;
+ const link = node && node.marks.find(m => m.type.name === "link");
if (link) {
- let href = link.attrs.href;
+ 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) {
- let linkDoc = await DocServer.GetRefField(linkclicked);
+ const linkDoc = await DocServer.GetRefField(linkclicked);
if (linkDoc instanceof Doc) {
- let anchor1 = await Cast(linkDoc.anchor1, Doc);
- let anchor2 = await Cast(linkDoc.anchor2, Doc);
- let currentDoc = SelectionManager.SelectedDocuments().length && SelectionManager.SelectedDocuments()[0].props.Document;
+ const anchor1 = await Cast(linkDoc.anchor1, Doc);
+ const anchor2 = await Cast(linkDoc.anchor2, Doc);
+ const currentDoc = SelectionManager.SelectedDocuments().length && SelectionManager.SelectedDocuments()[0].props.Document;
if (currentDoc && anchor1 && anchor2) {
if (Doc.AreProtosEqual(currentDoc, anchor1)) {
return StrCast(anchor2.title);
@@ -452,19 +375,32 @@ export class TooltipTextMenu {
}
}
- async createLinkDropdown() {
- let targetTitle = await this.getTextLinkTargetTitle();
- let input = document.createElement("input");
+ // 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
// TODO: integrate search to allow users to search for a doc to link to
- let linkInfo = new MenuItem({
+ const linkInfo = new MenuItem({
title: "",
execEvent: "",
class: "button-setting-disabled",
css: "",
render() {
- let p = document.createElement("p");
+ const p = document.createElement("p");
p.textContent = "Linked to:";
input.type = "text";
@@ -475,286 +411,156 @@ export class TooltipTextMenu {
input.focus();
};
- let div = document.createElement("div");
+ const div = document.createElement("div");
div.appendChild(p);
div.appendChild(input);
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
- let linkApply = new MenuItem({
+ const linkApply = new MenuItem({
title: "",
execEvent: "",
class: "",
css: "",
render() {
- let button = document.createElement("button");
+ const button = document.createElement("button");
button.className = "link-url-button";
button.textContent = "Apply hyperlink";
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;
}
});
// menu item to remove the link
// TODO: allow this to be undoable
- let self = this;
- let deleteLink = new MenuItem({
+ const self = this;
+ const deleteLink = new MenuItem({
title: "Delete link",
execEvent: "",
class: "separated-button",
css: "",
render() {
- let button = document.createElement("button");
+ const button = document.createElement("button");
button.textContent = "Remove link";
- let wrapper = document.createElement("div");
+ const wrapper = document.createElement("div");
wrapper.appendChild(button);
return wrapper;
},
enable() { return true; },
async run() {
- self.deleteLink();
- // update link dropdown
- let dropdown = await self.createLinkDropdown();
- let newLinkDropdowndom = dropdown.render(self.view).dom;
- self._linkDropdownDom && self.tooltip.replaceChild(newLinkDropdowndom, self._linkDropdownDom);
- self._linkDropdownDom = newLinkDropdowndom;
- }
- });
-
-
- let 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 => {
- let 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));
- let 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 = () => {
- let node = this.view.state.selection.$from.nodeAfter;
- let link = node && node.marks.find(m => m.type === this.view.state.schema.marks.link);
- let 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();
- }
- });
- }
-
- createLink() {
- let 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)) {
-
- let { from, $from, to, empty } = state.selection;
- let 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
- });
+ // 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;
}
});
- }
- //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
- let 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);
- }
-
- });
+ return new Dropdown(targetTitle ? [linkInfo, linkApply, deleteLink] : [linkInfo, linkApply], { class: "buttonSettings-dropdown" }) as MenuItem;
}
- public static insertStar(state: EditorState<any>, dispatch: any) {
- if (state.selection.empty) return false;
- let mark = state.schema.marks.highlight.create();
- let tr = state.tr;
- tr.addMark(state.selection.from, state.selection.to, mark);
- let content = tr.selection.content();
- let 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;
- let mark = state.schema.marks.highlight.create();
- let tr = state.tr;
- tr.addMark(state.selection.from, state.selection.to, mark);
- let content = tr.selection.content();
- let 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() {
- let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("viewBox", "-100 -100 650 650");
- let path = document.createElementNS('http://www.w3.org/2000/svg', "path");
+ const path = document.createElementNS('http://www.w3.org/2000/svg', "path");
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);
- let 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();
- let 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;
-
- let 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() {
// menu item for color picker
- let self = this;
- let colors = new MenuItem({
+ const self = this;
+ const colors = new MenuItem({
title: "",
execEvent: "",
class: "button-setting-disabled",
css: "",
render() {
- let p = document.createElement("p");
+ const p = document.createElement("p");
p.textContent = "Change highlight:";
- let colorsWrapper = document.createElement("div");
- colorsWrapper.className = "colorPicker-wrapper";
+ const colorsWrapper = TooltipTextMenu.createDiv("colorPicker-wrapper");
- let colors = [
+ const colors = [
PastelSchemaPalette.get("pink2"),
PastelSchemaPalette.get("purple4"),
PastelSchemaPalette.get("bluegreen1"),
@@ -768,29 +574,29 @@ export class TooltipTextMenu {
];
colors.forEach(color => {
- let button = document.createElement("button");
- button.className = color === TooltipTextMenuManager.Instance.highlight ? "colorPicker active" : "colorPicker";
+ const button = document.createElement("button");
+ 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
- let highlightDom = self.createHighlightTool().render(self.view).dom;
- let 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;
+ const highlightDom = self.createHighlightTool().render(self.view).dom;
+ const highlightDropdownDom = self.createHighlightDropdown().render(self.view).dom;
+ 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);
});
- let div = document.createElement("div");
+ const div = document.createElement("div");
div.appendChild(p);
div.appendChild(colorsWrapper);
return div;
@@ -801,62 +607,59 @@ export class TooltipTextMenu {
}
});
- let 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() {
- let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("viewBox", "-100 -100 650 650");
- let path = document.createElementNS('http://www.w3.org/2000/svg', "path");
+ const path = document.createElementNS('http://www.w3.org/2000/svg', "path");
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);
- let color = document.createElement("div");
- color.className = "buttonColor";
+ const color = TooltipTextMenu.createDiv("buttonColor");
color.style.backgroundColor = TooltipTextMenuManager.Instance.color.toString();
- let 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;
-
- let 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() {
// menu item for color picker
- let self = this;
- let colors = new MenuItem({
+ const self = this;
+ const colors = new MenuItem({
title: "",
execEvent: "",
class: "button-setting-disabled",
css: "",
render() {
- let p = document.createElement("p");
+ const p = document.createElement("p");
p.textContent = "Change color:";
- let colorsWrapper = document.createElement("div");
- colorsWrapper.className = "colorPicker-wrapper";
+ const colorsWrapper = TooltipTextMenu.createDiv("colorPicker-wrapper");
- let colors = [
+ const colors = [
DarkPastelSchemaPalette.get("pink2"),
DarkPastelSchemaPalette.get("purple4"),
DarkPastelSchemaPalette.get("bluegreen1"),
@@ -870,7 +673,7 @@ export class TooltipTextMenu {
];
colors.forEach(color => {
- let button = document.createElement("button");
+ const button = document.createElement("button");
button.className = color === TooltipTextMenuManager.Instance.color ? "colorPicker active" : "colorPicker";
if (color) {
button.style.backgroundColor = color;
@@ -880,8 +683,8 @@ export class TooltipTextMenu {
TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, self.view.state, self.view.dispatch);
// update color menu
- let colorDom = self.createColorTool().render(self.view).dom;
- let colorDropdownDom = self.createColorDropdown().render(self.view).dom;
+ const colorDom = self.createColorTool().render(self.view).dom;
+ const colorDropdownDom = self.createColorDropdown().render(self.view).dom;
self.colorDom && self.tooltip.replaceChild(colorDom, self.colorDom);
self.colorDropdownDom && self.tooltip.replaceChild(colorDropdownDom, self.colorDropdownDom);
self.colorDom = colorDom;
@@ -891,75 +694,72 @@ export class TooltipTextMenu {
colorsWrapper.appendChild(button);
});
- let div = document.createElement("div");
+ const div = document.createElement("div");
div.appendChild(p);
div.appendChild(colorsWrapper);
return div;
},
enable() { return false; },
- run(p1, p2, p3, event) {
- event.stopPropagation();
- }
+ run(p1, p2, p3, event) { event.stopPropagation(); }
});
- let 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"
};
- let self = this;
+ const self = this;
return new MenuItem({
title: "Brush tool",
label: "Brush tool",
icon: icon,
- css: "color:white;",
+ css: "fill:white;",
class: active ? "menuicon-active" : "menuicon",
execEvent: "",
run: (state, dispatch) => {
this.brush_function(state, dispatch);
// update dropdown with marks
- let newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom;
+ const newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom;
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 {
- let { from, to, $from } = this.view.state.selection;
+ const { from, to, $from } = this.view.state.selection;
if (this._brushdom) {
if (!this.view.state.selection.empty && $from && $from.nodeAfter) {
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;
@@ -971,40 +771,58 @@ 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";
}
-
- let brushInfo = new MenuItem({
+ const brushInfo = new MenuItem({
title: "",
label: label,
execEvent: "",
class: "button-setting-disabled",
css: "",
enable() { return false; },
- run(p1, p2, p3, event) {
- event.stopPropagation();
- }
+ run(p1, p2, p3, event) { event.stopPropagation(); }
});
- let self = this;
- let clearBrush = new MenuItem({
+ const self = this;
+ const input = document.createElement("input");
+ const clearBrush = new MenuItem({
title: "Clear brush",
execEvent: "",
class: "separated-button",
css: "",
render() {
- let button = document.createElement("button");
+ const button = document.createElement("button");
button.textContent = "Clear brush";
- let wrapper = document.createElement("div");
+ 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;
},
@@ -1015,305 +833,41 @@ export class TooltipTextMenu {
// update brush tool
// TODO: this probably isn't very clean
- let 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;
- let newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom;
+ const newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom;
self._brushDropdownDom && self.tooltip.replaceChild(newBrushDropdowndom, self._brushDropdownDom);
self._brushDropdownDom = newBrushDropdowndom;
}
});
- let hasMarks = TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMarks.size > 0;
- let brushDom = new Dropdown(hasMarks ? [brushInfo, clearBrush] : [brushInfo], { class: "buttonSettings-dropdown" }) as MenuItem;
- return brushDom;
+ const hasMarks = TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMarks.size > 0;
+ 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[]) => {
- let { $cursor, ranges } = view.state.selection as TextSelection;
- let state = view.state;
- let 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++) {
- let { $from, $to } = ranges[i];
- has = state.doc.rangeHasMark($from.pos, $to.pos, type);
- }
- for (let 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) {
- let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
- { ...(view.state.selection as NodeSelection).node.attrs, setFontFamily: 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) => {
- let { $cursor, ranges } = view.state.selection as TextSelection;
- let state = view.state;
- let dispatch = view.dispatch;
-
- //remove all other active font marks
- if ($cursor) {
- if (view.state.schema.marks.pFontFamily.isInSet(state.storedMarks || $cursor.marks())) {
- dispatch(state.tr.removeStoredMark(view.state.schema.marks.pFontFamily));
- }
- } else {
- let has = false;
- for (let i = 0; !has && i < ranges.length; i++) {
- let { $from, $to } = ranges[i];
- has = state.doc.rangeHasMark($from.pos, $to.pos, view.state.schema.marks.pFontFamily);
- }
- for (let i of ranges) {
- if (has) {
- toggleMark(view.state.schema.marks.pFontFamily)(view.state, view.dispatch, view);
- }
- }
- }
- let fontName = mark.attrs.family;
- if (fontName) { this.updateFontStyleDropdown(fontName); }
- if (this.editorProps) {
- let ruleProvider = this.editorProps.ruleProvider;
- let heading = NumCast(this.editorProps.Document.heading);
- if (ruleProvider && heading) {
- ruleProvider["ruleFont_" + heading] = fontName;
- }
- }
- //actually apply font
- if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
- let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
- { ...(view.state.selection as NodeSelection).node.attrs, setFontFamily: fontName }), view.state.schema);
- view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
- }
- else view.dispatch(view.state.tr.addMark(view.state.selection.from, view.state.selection.to, view.state.schema.marks.pFontFamily.create({ family: fontName })));
- view.state.storedMarks = [...(view.state.storedMarks || []), view.state.schema.marks.pFontFamily.create({ family: fontName })];
- }
-
- changeToFontSize = (mark: Mark, view: EditorView) => {
- let { $cursor, ranges } = view.state.selection as TextSelection;
- let state = view.state;
- let dispatch = view.dispatch;
-
- //remove all other active font marks
- if ($cursor) {
- if (view.state.schema.marks.pFontSize.isInSet(state.storedMarks || $cursor.marks())) {
- dispatch(state.tr.removeStoredMark(view.state.schema.marks.pFontSize));
- }
- } else {
- let has = false;
- for (let i = 0; !has && i < ranges.length; i++) {
- let { $from, $to } = ranges[i];
- has = state.doc.rangeHasMark($from.pos, $to.pos, view.state.schema.marks.pFontSize);
- }
- for (let i of ranges) {
- if (has) {
- toggleMark(view.state.schema.marks.pFontSize)(view.state, view.dispatch, view);
- }
- }
- }
-
- let size = mark.attrs.fontSize;
- if (size) { this.updateFontSizeDropdown(String(size) + " pt"); }
- if (this.editorProps) {
- let ruleProvider = this.editorProps.ruleProvider;
- let heading = NumCast(this.editorProps.Document.heading);
- if (ruleProvider && heading) {
- ruleProvider["ruleSize_" + heading] = size;
- }
- }
- //actually apply font
- if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
- let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
- { ...(view.state.selection as NodeSelection).node.attrs, setFontSize: size }), view.state.schema);
- view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
- }
- else view.dispatch(view.state.tr.addMark(view.state.selection.from, view.state.selection.to, view.state.schema.marks.pFontSize.create({ fontSize: size })));
- view.state.storedMarks = [...(view.state.storedMarks || []), view.state.schema.marks.pFontSize.create({ fontSize: size })];
- }
-
- //remove all node typeand apply the passed-in one to the selected text
- changeToNodeType = (nodeType: NodeType | undefined) => {
- //remove oldif (nodeType) { //add new
- let view = this.view;
- if (nodeType === schema.nodes.bullet_list) {
- wrapInList(nodeType)(view.state, view.dispatch);
- } else {
- var marks = view.state.storedMarks || (view.state.selection.$to.parentOffset && view.state.selection.$from.marks());
- if (!wrapInList(schema.nodes.ordered_list)(view.state, (tx2: any) => {
- let tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle);
- marks && tx3.ensureMarks([...marks]);
- marks && tx3.setStoredMarks([...marks]);
-
- view.dispatch(tx2);
- })) {
- let tx2 = view.state.tr;
- let 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>>) {
- let { 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) {
- let 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) {
- let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
- svg.setAttribute("viewBox", "-100 -100 650 650");
- let path = document.createElementNS('http://www.w3.org/2000/svg', "path");
- path.setAttributeNS(null, "d", dpath);
- svg.appendChild(path);
-
- let 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>>) {
- let $from = state.selection.$from;
- for (let d = $from.depth; d >= 0; d--) {
- let 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) {
- let attrs = {};
-
- if (state.selection instanceof NodeSelection) {
- const sel: NodeSelection = state.selection;
- let $from = sel.$from;
- let to = sel.to;
- let 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>) {
- 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 => {
- 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() {
- let 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) {
@@ -1322,71 +876,52 @@ export class TooltipTextMenu {
return;
}
this.view = view;
- let 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
- let linkDropdown = await this.createLinkDropdown();
- let newLinkDropdowndom = linkDropdown.render(this.view).dom;
- this._linkDropdownDom && this.tooltip.replaceChild(newLinkDropdowndom, this._linkDropdownDom);
- this._linkDropdownDom = newLinkDropdowndom;
-
- //UPDATE FONT STYLE DROPDOWN
- let 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
- let 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)) {
- let 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
activeFontSizeOnSelection() {
//current selection
- let state = this.view.state;
- let activeSizes: number[] = [];
+ const state = this.view.state;
+ const activeSizes: number[] = [];
const pos = this.view.state.selection.$from;
const ref_node: ProsNode = this.reference_node(pos);
if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) {
@@ -1397,8 +932,8 @@ export class TooltipTextMenu {
//finds fontSize at start of selection
activeFontFamilyOnSelection() {
//current selection
- let state = this.view.state;
- let activeFamilies: string[] = [];
+ const state = this.view.state;
+ const activeFamilies: string[] = [];
const pos = this.view.state.selection.$from;
const ref_node: ProsNode = this.reference_node(pos);
if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) {
@@ -1407,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
- let { empty, ranges, $to } = this.view.state.selection as TextSelection;
- let state = this.view.state;
- let dispatch = this.view.dispatch;
- let activeMarks: MarkType[];
+ const { empty, ranges, $to } = this.view.state.selection as TextSelection;
+ const state = this.view.state;
+ let activeMarks: MarkType[] = [];
if (!empty) {
activeMarks = markGroup.filter(mark => {
- let has = false;
+ const has = false;
for (let i = 0; !has && i < ranges.length; i++) {
- let { $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;
@@ -1435,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);
}
- let mark = state.schema.mark(mark_type);
+ const mark = state.schema.mark(mark_type);
return ref_node.marks.includes(mark);
- return false;
});
}
- else {
- return [];
- }
-
}
return activeMarks;
}
@@ -1485,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;
@@ -1509,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; }
}