From bedfd7e30c4c331628968ea62fef736a1d2cd542 Mon Sep 17 00:00:00 2001 From: Fawn Date: Thu, 10 Oct 2019 10:35:11 -0400 Subject: added manager for managing brush tool state on rich text editor + updated styling to match more with pdf --- src/client/util/TooltipTextMenu.tsx | 566 ++++++++++++++++++++++++------------ 1 file changed, 374 insertions(+), 192 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index c82d3bc63..6070d2ef4 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -17,79 +17,99 @@ import { DragManager } from "./DragManager"; import { LinkManager } from "./LinkManager"; import { schema } from "./RichTextSchema"; import "./TooltipTextMenu.scss"; -import { Cast, NumCast } from '../../new_fields/Types'; +import { Cast, NumCast, StrCast } from '../../new_fields/Types'; import { updateBullets } from './ProsemirrorExampleTransfer'; import { DocumentDecorations } from '../views/DocumentDecorations'; +import { SelectionManager } from './SelectionManager'; const { toggleMark, setBlockType } = require("prosemirror-commands"); const { openPrompt, TextField } = require("./ProsemirrorCopy/prompt.js"); //appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc. export class TooltipTextMenu { - public tooltip: HTMLElement; + // editor state private view: EditorView; private editorProps: FieldViewProps & FormattedTextBoxProps | undefined; - private fontStyles: MarkType[]; - private fontSizes: MarkType[]; - private listTypes: (NodeType | any)[]; - private fontSizeToNum: Map; - private fontStylesToName: Map; - private listTypeToIcon: Map; + private fontStyles: MarkType[] = []; + private fontSizes: MarkType[] = []; + private listTypes: (NodeType | any)[] = []; + private fontSizeToNum: Map = new Map(); + private fontStylesToName: Map = new Map(); + private listTypeToIcon: Map = new Map(); + private _activeMarks: Mark[] = []; + private _marksToDoms: Map = new Map(); + // private _brushMarks?: Set; //private link: HTMLAnchorElement; - private wrapper: HTMLDivElement; - private extras: HTMLDivElement; + + // editor doms + public tooltip: HTMLElement = document.createElement("div"); + private wrapper: HTMLDivElement = document.createElement("div"); private linkEditor?: HTMLDivElement; private linkText?: HTMLDivElement; private linkDrag?: HTMLImageElement; - //dropdown doms + private _linkDropdownDom?: Node; + private _brushdom?: Node; + private fontSizeDom?: Node; private fontStyleDom?: Node; private listTypeBtnDom?: Node; - private _activeMarks: Mark[] = []; - - private _collapseBtn?: MenuItem; + private _collapsed: boolean = false; + // private _collapseBtn?: MenuItem; + // private _brushIsEmpty: boolean = true; - private _brushMarks?: Set; - private _brushIsEmpty: boolean = true; - private _brushdom?: Node; - private _marksToDoms: Map = new Map(); + public static Toolbar: HTMLDivElement | undefined; - private _collapsed: boolean = false; constructor(view: EditorView) { this.view = view; - this.wrapper = document.createElement("div"); - this.tooltip = document.createElement("div"); - this.extras = document.createElement("div"); + // replace old active menu with this + if (TooltipTextMenuManager.Instance.activeMenu) { + TooltipTextMenuManager.Instance.activeMenu.wrapper.remove(); + } + TooltipTextMenuManager.Instance.activeMenu = this; - this.wrapper.appendChild(this.extras); - this.wrapper.appendChild(this.tooltip); + // initialize the tooltip + this.createTooltip(view); - this.tooltip.className = "tooltipMenu"; - this.extras.className = "tooltipExtras"; + // initialize the wrapper + this.wrapper = document.createElement("div"); this.wrapper.className = "wrapper"; + this.wrapper.appendChild(this.tooltip); - const dragger = document.createElement("span"); - dragger.className = "dragger"; - dragger.textContent = ">>>"; - this.extras.appendChild(dragger); - - this.dragElement(dragger); + // positioning? + TooltipTextMenu.Toolbar = this.wrapper; - // this.createCollapse(); - // if (this._collapseBtn) { - // this.tooltip.appendChild(this._collapseBtn.render(this.view).dom); + // // position wrapper + // if (TooltipTextMenuManager.Instance.isPinned) { + // this.wrapper.style.position = "absolute"; + // this.wrapper.style.left = TooltipTextMenuManager.Instance.pinnedX + "px"; + // this.wrapper.style.top = TooltipTextMenuManager.Instance.pinnedY + "px"; + + // // if pinned, append to mainview + // const mainView = document.querySelector("#main-div"); + // mainView && mainView.appendChild(this.wrapper); + // } else { + // this.wrapper.style.position = "absolute"; + // this.wrapper.style.top = (this.wrapper.offsetTop + TooltipTextMenuManager.Instance.unpinnedY) + "px"; + // this.wrapper.style.left = (this.wrapper.offsetLeft + TooltipTextMenuManager.Instance.unpinnedX) + "px"; + + // // add tooltip to outerdiv to circumvent scaling problem + // const outer_div = this.editorProps.outer_div; + // outer_div && outer_div(this.wrapper); // } - //add the div which is the tooltip - //view.dom.parentNode!.parentNode!.appendChild(this.tooltip); - //add additional icons - library.add(faListUl); - //add the buttons to the tooltip + } + + private async createTooltip(view: EditorView) { + // initialize element + this.tooltip = document.createElement("div"); + this.tooltip.className = "tooltipMenu"; + + // init buttons to the tooltip let items = [ { command: toggleMark(schema.marks.strong), dom: this.icon("B", "strong", "Bold") }, { command: toggleMark(schema.marks.em), dom: this.icon("i", "em", "Italic") }, @@ -100,8 +120,8 @@ export class TooltipTextMenu { { command: toggleMark(schema.marks.highlight), dom: this.icon("H", 'blue', 'Blue') } ]; + // add menu items this._marksToDoms = new Map(); - //add menu items items.forEach(({ dom, command }) => { this.tooltip.appendChild(dom); switch (dom.title) { @@ -131,10 +151,46 @@ export class TooltipTextMenu { }); }); + + // link menu this.updateLinkMenu(); + let dropdown = await this.createLinkDropdown(); + let newLinkDropdowndom = dropdown.render(this.view).dom; + this._linkDropdownDom && this.tooltip.replaceChild(newLinkDropdowndom, this._linkDropdownDom); + this._linkDropdownDom = newLinkDropdowndom; + + // 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 and pin + this.tooltip.appendChild(this.createLink().render(this.view).dom); + this.tooltip.appendChild(this.createStar().render(this.view).dom); + // + this.updateListItemDropdown(":", this.listTypeBtnDom); + + // + this.updateFromDash(view, undefined, undefined); + // TooltipTextMenu.Toolbar = this.wrapper; - //list of font styles + // dragger + // TODO: onclick handler in drag handles collapsing + this.createDragger(); + } + + initFontStyles() { this.fontStylesToName = new Map(); this.fontStylesToName.set(schema.marks.timesNewRoman, "Times New Roman"); this.fontStylesToName.set(schema.marks.arial, "Arial"); @@ -144,8 +200,9 @@ export class TooltipTextMenu { this.fontStylesToName.set(schema.marks.impact, "Impact"); this.fontStylesToName.set(schema.marks.crimson, "Crimson Text"); this.fontStyles = Array.from(this.fontStylesToName.keys()); + } - //font sizes + initFontSizes() { this.fontSizeToNum = new Map(); this.fontSizeToNum.set(schema.marks.p10, 10); this.fontSizeToNum.set(schema.marks.p12, 12); @@ -158,39 +215,41 @@ export class TooltipTextMenu { this.fontSizeToNum.set(schema.marks.p48, 48); this.fontSizeToNum.set(schema.marks.p72, 72); this.fontSizeToNum.set(schema.marks.pFontSize, 10); - // this.fontSizeToNum.set(schema.marks.pFontSize, 12); - // this.fontSizeToNum.set(schema.marks.pFontSize, 14); - // this.fontSizeToNum.set(schema.marks.pFontSize, 16); - // this.fontSizeToNum.set(schema.marks.pFontSize, 18); - // this.fontSizeToNum.set(schema.marks.pFontSize, 20); - // this.fontSizeToNum.set(schema.marks.pFontSize, 24); - // this.fontSizeToNum.set(schema.marks.pFontSize, 32); - // this.fontSizeToNum.set(schema.marks.pFontSize, 48); - // this.fontSizeToNum.set(schema.marks.pFontSize, 72); this.fontSizes = Array.from(this.fontSizeToNum.keys()); + } - //list types + initListTypes() { this.listTypeToIcon = new Map(); this.listTypeToIcon.set(schema.nodes.bullet_list, ":"); 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()); + } - //custom tools - // this.tooltip.appendChild(this.createLink().render(this.view).dom); + createDragger() { + const dragger = document.createElement("div"); + dragger.className = "dragger"; - this._brushdom = this.createBrush().render(this.view).dom; - this.tooltip.appendChild(this._brushdom); - this.tooltip.appendChild(this.createLink().render(this.view).dom); - this.tooltip.appendChild(this.createStar().render(this.view).dom); + let draggerWrapper = document.createElement("div"); + draggerWrapper.className = "dragger-wrapper"; - this.updateListItemDropdown(":", this.listTypeBtnDom); + 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"; - this.updateFromDash(view, undefined, undefined); - TooltipTextMenu.Toolbar = this.wrapper; + draggerWrapper.appendChild(line1); + draggerWrapper.appendChild(line2); + draggerWrapper.appendChild(line3); + + dragger.appendChild(draggerWrapper); + + this.tooltip.appendChild(dragger); + this.dragElement(dragger); } - public static Toolbar: HTMLDivElement | undefined; //label of dropdown will change to given label updateFontSizeDropdown(label: string) { @@ -243,24 +302,13 @@ export class TooltipTextMenu { if (!this.linkEditor || !this.linkText) { this.linkEditor = document.createElement("div"); this.linkEditor.className = "ProseMirror-icon menuicon"; - this.linkEditor.style.color = "black"; - this.linkText = document.createElement("div"); - this.linkText.style.cssFloat = "left"; - this.linkText.style.marginRight = "5px"; - this.linkText.style.marginLeft = "5px"; - 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) => { + this.linkDrag = document.createElement("img"); + this.linkDrag.src = "https://seogurusnyc.com/wp-content/uploads/2016/12/link-1.png"; + this.linkDrag.style.width = "13px"; + this.linkDrag.style.height = "13px"; + this.linkDrag.title = "Drag to create link"; + this.linkDrag.id = "link-drag"; + this.linkDrag.onpointerdown = (e: PointerEvent) => { let node = this.view.state.selection.$from.nodeAfter; let link = node && node.marks.find(m => m.type.name === "link"); if (link) { @@ -281,62 +329,137 @@ export class TooltipTextMenu { 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.style.color = "black"; - this.linkDrag.style.background = "black"; - this.linkDrag.style.cssFloat = "left"; - 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, 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(); + // } + // }; + // // this.tooltip.appendChild(this.linkEditor); + } + + async getTextLinkTargetTitle() { 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-"; + if (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) { + let 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; + if (currentDoc && anchor1 && anchor2) { + if (Doc.AreProtosEqual(currentDoc, anchor1)) return StrCast(anchor2.title); + if (Doc.AreProtosEqual(currentDoc, anchor2)) return StrCast(anchor2.title); + } + } + } + } else { + return href; + } + } + } + } - this.linkText.onkeydown = (e: KeyboardEvent) => { - if (e.key === "Enter") { - // this.makeLink(this.linkText!.textContent!); - e.stopPropagation(); - e.preventDefault(); + async createLinkDropdown() { + let targetTitle = await this.getTextLinkTargetTitle(); + let 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({ + title: "", + execEvent: "", + class: "button-setting-disabled", + css: "", + render() { + let p = document.createElement("p"); + p.textContent = "Linked to:"; + + input.type = "text"; + input.placeholder = "Enter URL"; + if (targetTitle) input.value = targetTitle; + input.onclick = (e: MouseEvent) => { + input.select(); + input.focus(); + }; + + let div = document.createElement("div"); + div.appendChild(p); + div.appendChild(input); + return div; + }, + enable() { return false; }, + run(p1, p2, p3, event) { + event.stopPropagation(); } - }; - // this.tooltip.appendChild(this.linkEditor); + }); + + // menu item to update/apply the hyperlink to the selected text + let linkApply = new MenuItem({ + title: "", + execEvent: "", + class: "", + css: "", + render() { + let button = document.createElement("button"); + button.className = "link-url-button"; + button.textContent = "Apply hyperlink"; + return button; + }, + enable() { return false; }, + run: (state, dispatch, view, event) => { + event.stopPropagation(); + this.makeLink(input.value, "onRight"); + } + }); + + // menu item to remove the link + // TODO: allow this to be undoable + let self = this; + let deleteLink = new MenuItem({ + title: "Delete link", + execEvent: "", + class: "separated-button", + css: "", + render() { + let button = document.createElement("button"); + button.textContent = "Remove link"; + + let 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; } + dragElement(elmnt: HTMLElement) { var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; if (elmnt) { @@ -627,15 +750,15 @@ export class TooltipTextMenu { // selectionchanged event handler brush_function(state: EditorState, dispatch: any) { - if (this._brushIsEmpty) { + if (TooltipTextMenuManager.Instance._brushIsEmpty) { const selected_marks = this.getMarksInSelection(this.view.state); if (this._brushdom) { if (selected_marks.size >= 0) { - this._brushMarks = selected_marks; + TooltipTextMenuManager.Instance._brushMarks = selected_marks; const newbrush = this.createBrush(true).render(this.view).dom; this.tooltip.replaceChild(newbrush, this._brushdom); this._brushdom = newbrush; - this._brushIsEmpty = !this._brushIsEmpty; + TooltipTextMenuManager.Instance._brushIsEmpty = !TooltipTextMenuManager.Instance._brushIsEmpty; } } } @@ -643,9 +766,9 @@ export class TooltipTextMenu { let { from, to, $from } = this.view.state.selection; if (this._brushdom) { if (!this.view.state.selection.empty && $from && $from.nodeAfter) { - if (this._brushMarks && to - from > 0) { + if (TooltipTextMenuManager.Instance._brushMarks && to - from > 0) { this.view.dispatch(this.view.state.tr.removeMark(from, to)); - Array.from(this._brushMarks).filter(m => m.type !== schema.marks.user_mark).forEach((mark: Mark) => { + 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, []); }); @@ -655,7 +778,7 @@ export class TooltipTextMenu { const newbrush = this.createBrush(false).render(this.view).dom; this.tooltip.replaceChild(newbrush, this._brushdom); this._brushdom = newbrush; - this._brushIsEmpty = !this._brushIsEmpty; + TooltipTextMenuManager.Instance._brushIsEmpty = !TooltipTextMenuManager.Instance._brushIsEmpty; } } } @@ -663,46 +786,46 @@ export class TooltipTextMenu { } - createCollapse() { - this._collapseBtn = new MenuItem({ - title: "Collapse", - //label: "Collapse", - icon: icons.join, - execEvent: "", - css: "color:white;", - class: "summarize", - run: () => { - this.collapseToolTip(); - } - }); - } + // createCollapse() { + // this._collapseBtn = new MenuItem({ + // title: "Collapse", + // //label: "Collapse", + // icon: icons.join, + // execEvent: "", + // css: "color:white;", + // class: "summarize", + // run: () => { + // this.collapseToolTip(); + // } + // }); + // } - collapseToolTip() { - if (this._collapseBtn) { - if (this._collapseBtn.spec.title === "Collapse") { - // const newcollapseBtn = new MenuItem({ - // title: "Expand", - // icon: icons.join, - // execEvent: "", - // css: "color:white;", - // class: "summarize", - // run: (state, dispatch, view) => { - // this.collapseToolTip(); - // } - // }); - // this.tooltip.replaceChild(newcollapseBtn.render(this.view).dom, this._collapseBtn.render(this.view).dom); - // this._collapseBtn = newcollapseBtn; - this.tooltip.style.width = "30px"; - this._collapseBtn.spec.title = "Expand"; - this._collapseBtn.render(this.view); - } - else { - this._collapseBtn.spec.title = "Collapse"; - this.tooltip.style.width = "550px"; - this._collapseBtn.render(this.view); - } - } - } + // collapseToolTip() { + // if (this._collapseBtn) { + // if (this._collapseBtn.spec.title === "Collapse") { + // // const newcollapseBtn = new MenuItem({ + // // title: "Expand", + // // icon: icons.join, + // // execEvent: "", + // // css: "color:white;", + // // class: "summarize", + // // run: (state, dispatch, view) => { + // // this.collapseToolTip(); + // // } + // // }); + // // this.tooltip.replaceChild(newcollapseBtn.render(this.view).dom, this._collapseBtn.render(this.view).dom); + // // this._collapseBtn = newcollapseBtn; + // this.tooltip.style.width = "30px"; + // this._collapseBtn.spec.title = "Expand"; + // this._collapseBtn.render(this.view); + // } + // else { + // this._collapseBtn.spec.title = "Collapse"; + // this.tooltip.style.width = "550px"; + // this._collapseBtn.render(this.view); + // } + // } + // } createLink() { let markType = schema.marks.link; @@ -913,32 +1036,41 @@ export class TooltipTextMenu { update_mark_doms() { this.reset_mark_doms(); let foundlink = false; - let children = this.extras.childNodes; + // let children = this.extras.childNodes; this._activeMarks.forEach((mark) => { if (this._marksToDoms.has(mark)) { let dom = this._marksToDoms.get(mark); if (dom) dom.style.color = "greenyellow"; } - if (children.length > 1) { - foundlink = true; - } - if (mark.type.name === "link" && children.length === 1) { - // let del = document.createElement("button"); - // del.textContent = "X"; - // del.style.color = "red"; - // del.style.height = "10px"; - // del.style.width = "10px"; - // del.style.marginLeft = "5px"; - // del.onclick = this.deleteLink; - // this.extras.appendChild(del); - let del = this.deleteLinkItem().render(this.view).dom; - this.extras.appendChild(del); - foundlink = true; - } + // if (children.length > 1) { + // foundlink = true; + // } + // if (mark.type.name === "link" && children.length === 1) { + // // let del = document.createElement("button"); + // // del.textContent = "X"; + // // del.style.color = "red"; + // // del.style.height = "10px"; + // // del.style.width = "10px"; + // // del.style.marginLeft = "5px"; + // // del.onclick = this.deleteLink; + // // this.extras.appendChild(del); + // let del = this.deleteLinkItem().render(this.view).dom; + // this.extras.appendChild(del); + // foundlink = true; + // } }); - if (!foundlink) { - if (children.length > 1) { - this.extras.removeChild(children[1]); + // if (!foundlink) { + // if (children.length > 1) { + // this.extras.removeChild(children[1]); + // } + // } + + // 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; } } @@ -1022,3 +1154,53 @@ export class TooltipTextMenu { this.wrapper.remove(); } } + + +class TooltipTextMenuManager { + private static _instance: TooltipTextMenuManager; + + public pinnedX: number = 0; + public pinnedY: number = 0; + public unpinnedX: number = 0; + public unpinnedY: number = 0; + private _isPinned: boolean = false; + + public _brushMarks: Set | undefined; + public _brushIsEmpty: boolean = true; + + public activeMenu: TooltipTextMenu | undefined; + + static get Instance() { + if (!TooltipTextMenuManager._instance) { + TooltipTextMenuManager._instance = new TooltipTextMenuManager(); + } + return TooltipTextMenuManager._instance; + } + + // private pinnedToUnpinned() { + // let position = MainOverlayTextBox.Instance.position; + + // this.unpinnedX = this.pinnedX - position[0]; + // this.unpinnedY = this.pinnedY - position[1]; + // } + + // private unpinnedToPinned() { + // let position = MainOverlayTextBox.Instance.position; + + // this.pinnedX = position[0] + this.unpinnedX; + // this.pinnedY = position[1] + this.unpinnedY; + // } + + public get isPinned() { + return this._isPinned; + } + + public toggleIsPinned() { + // if (this._isPinned) { + // this.pinnedToUnpinned(); + // } else { + // this.unpinnedToPinned(); + // } + this._isPinned = !this._isPinned; + } +} -- cgit v1.2.3-70-g09d2 From dbf41b6eaaebe16567289ba4814316b2c79e0b65 Mon Sep 17 00:00:00 2001 From: Fawn Date: Thu, 10 Oct 2019 10:50:53 -0400 Subject: richtexteditor menuicon styling --- src/client/util/TooltipTextMenu.scss | 132 ------------- src/client/util/TooltipTextMenu.tsx | 272 ++++++++++++++------------- src/client/views/nodes/FormattedTextBox.scss | 22 +-- 3 files changed, 153 insertions(+), 273 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss index 6bafdbbfd..11c937471 100644 --- a/src/client/util/TooltipTextMenu.scss +++ b/src/client/util/TooltipTextMenu.scss @@ -55,12 +55,6 @@ } } -// .ProseMirror-menu-submenu { -// display: none; -// min-width: 4em; -// left: 100%; -// top: -3px; -// } .ProseMirror-menu-submenu-label:after { content: ""; @@ -72,34 +66,6 @@ right: 4px; top: calc(50% - 4px); } - - - -// .ProseMirror-menu-active { -// background: #eee; -// border-radius: 4px; -// } - -// .ProseMirror-menu-disabled { -// opacity: .3; -// } - - -// .ProseMirror-menubar { -// border-top-left-radius: inherit; -// border-top-right-radius: inherit; -// position: relative; -// min-height: 1em; -// color: white; -// padding: 10px 10px; -// top: 0; left: 0; right: 0; -// border-bottom: 1px solid silver; -// background:$dark-color; -// z-index: 10; -// -moz-box-sizing: border-box; -// box-sizing: border-box; -// overflow: visible; -// } .ProseMirror-icon { display: inline-block; @@ -121,74 +87,6 @@ vertical-align: text-top; } } - -// .ProseMirror ul, .ProseMirror ol { -// padding-left: 30px; -// } - -// .ProseMirror blockquote { -// padding-left: 1em; -// border-left: 3px solid #eee; -// margin-left: 0; margin-right: 0; -// } - -// .ProseMirror-example-setup-style img { -// cursor: default; -// } - -// .ProseMirror-prompt { -// background: white; -// padding: 5px 10px 5px 15px; -// border: 1px solid silver; -// position: fixed; -// border-radius: 3px; -// z-index: 11; -// box-shadow: -.5px 2px 5px white(255, 255, 255, 0.2); -// } - -// .ProseMirror-prompt h5 { -// margin: 0; -// font-weight: normal; -// font-size: 100%; -// color: #444; -// } - -// .ProseMirror-prompt input[type="text"], -// .ProseMirror-prompt textarea { -// background: white; -// border: none; -// outline: none; -// } - -// .ProseMirror-prompt input[type="text"] { -// padding: 0 4px; -// } - -// .ProseMirror-prompt-close { -// position: absolute; -// left: 2px; top: 1px; -// color: #666; -// border: none; background: transparent; padding: 0; -// } - -// .ProseMirror-prompt-close:after { -// content: "✕"; -// font-size: 12px; -// } - -// .ProseMirror-invalid { -// background: #ffc; -// border: 1px solid #cc7; -// border-radius: 4px; -// padding: 5px 10px; -// position: absolute; -// min-width: 10em; -// } - -// .ProseMirror-prompt-buttons { -// margin-top: 5px; -// display: none; -// } .wrapper { position: absolute; @@ -224,36 +122,6 @@ } } -// .tooltipExtras { -// position: absolute; -// z-index: 20000; -// background: #121721; -// border: 1px solid silver; -// border-radius: 15px; -// //height: 60px; -// //padding: 2px 10px; -// //margin-top: 100px; -// //-webkit-transform: translateX(-50%); -// //transform: translateX(-50%); -// transform: translateY(-115px); -// pointer-events: all; -// height: 25px; -// width:fit-content; -// .ProseMirror-example-setup-style hr { -// padding: 2px 10px; -// border: none; -// margin: 1em 0; -// } - -// .ProseMirror-example-setup-style hr:after { -// content: ""; -// display: block; -// height: 1px; -// background-color: silver; -// line-height: 2px; -// } -// } - #link-drag { background-color: #323232; } diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 6070d2ef4..d0bb753ba 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -154,10 +154,10 @@ export class TooltipTextMenu { // link menu this.updateLinkMenu(); - let dropdown = await this.createLinkDropdown(); - let newLinkDropdowndom = dropdown.render(this.view).dom; - this._linkDropdownDom && this.tooltip.replaceChild(newLinkDropdowndom, this._linkDropdownDom); - this._linkDropdownDom = newLinkDropdowndom; + // let dropdown = await this.createLinkDropdown(); + // let newLinkDropdowndom = dropdown.render(this.view).dom; + // this._linkDropdownDom && this.tooltip.replaceChild(newLinkDropdowndom, this._linkDropdownDom); + // this._linkDropdownDom = newLinkDropdowndom; // list of font styles this.initFontStyles(); @@ -302,13 +302,24 @@ export class TooltipTextMenu { if (!this.linkEditor || !this.linkText) { this.linkEditor = document.createElement("div"); this.linkEditor.className = "ProseMirror-icon menuicon"; - this.linkDrag = document.createElement("img"); - this.linkDrag.src = "https://seogurusnyc.com/wp-content/uploads/2016/12/link-1.png"; - this.linkDrag.style.width = "13px"; - this.linkDrag.style.height = "13px"; - this.linkDrag.title = "Drag to create link"; - this.linkDrag.id = "link-drag"; - this.linkDrag.onpointerdown = (e: PointerEvent) => { + this.linkEditor.style.color = "black"; + this.linkText = document.createElement("div"); + this.linkText.style.cssFloat = "left"; + this.linkText.style.marginRight = "5px"; + this.linkText.style.marginLeft = "5px"; + 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) { @@ -329,136 +340,137 @@ export class TooltipTextMenu { 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.style.color = "black"; + this.linkDrag.style.background = "black"; + this.linkDrag.style.cssFloat = "left"; + 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, 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(); - // } - // }; - // // this.tooltip.appendChild(this.linkEditor); - } - - async getTextLinkTargetTitle() { let node = this.view.state.selection.$from.nodeAfter; let link = node && node.marks.find(m => m.type.name === "link"); - if (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) { - let 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; - if (currentDoc && anchor1 && anchor2) { - if (Doc.AreProtosEqual(currentDoc, anchor1)) return StrCast(anchor2.title); - if (Doc.AreProtosEqual(currentDoc, anchor2)) return StrCast(anchor2.title); - } - } - } - } else { - return href; - } - } - } - } - - async createLinkDropdown() { - let targetTitle = await this.getTextLinkTargetTitle(); - let input = document.createElement("input"); + this.linkText.textContent = link ? link.attrs.href : "-empty-"; - // 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({ - title: "", - execEvent: "", - class: "button-setting-disabled", - css: "", - render() { - let p = document.createElement("p"); - p.textContent = "Linked to:"; - - input.type = "text"; - input.placeholder = "Enter URL"; - if (targetTitle) input.value = targetTitle; - input.onclick = (e: MouseEvent) => { - input.select(); - input.focus(); - }; - - let div = document.createElement("div"); - div.appendChild(p); - div.appendChild(input); - return div; - }, - enable() { return false; }, - run(p1, p2, p3, event) { - event.stopPropagation(); - } - }); - - // menu item to update/apply the hyperlink to the selected text - let linkApply = new MenuItem({ - title: "", - execEvent: "", - class: "", - css: "", - render() { - let button = document.createElement("button"); - button.className = "link-url-button"; - button.textContent = "Apply hyperlink"; - return button; - }, - enable() { return false; }, - run: (state, dispatch, view, event) => { - event.stopPropagation(); - this.makeLink(input.value, "onRight"); - } - }); - - // menu item to remove the link - // TODO: allow this to be undoable - let self = this; - let deleteLink = new MenuItem({ - title: "Delete link", - execEvent: "", - class: "separated-button", - css: "", - render() { - let button = document.createElement("button"); - button.textContent = "Remove link"; - - let 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; + this.linkText.onkeydown = (e: KeyboardEvent) => { + if (e.key === "Enter") { + // this.makeLink(this.linkText!.textContent!); + e.stopPropagation(); + e.preventDefault(); } - }); + }; + // this.tooltip.appendChild(this.linkEditor); + } + // updateLinkMenu() { + // if (!this.linkEditor || !this.linkText) { + // this.linkEditor = document.createElement("div"); + // this.linkEditor.className = "ProseMirror-icon menuicon"; + // this.linkDrag = document.createElement("img"); + // this.linkDrag.src = "https://seogurusnyc.com/wp-content/uploads/2016/12/link-1.png"; + // this.linkDrag.style.width = "13px"; + // this.linkDrag.style.height = "13px"; + // this.linkDrag.title = "Drag to create link"; + // this.linkDrag.id = "link-drag"; + // this.linkDrag.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) => { + // 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.linkEditor.appendChild(this.linkDrag); + // this.tooltip.appendChild(this.linkEditor); + // } - let linkDropdown = new Dropdown(targetTitle ? [linkInfo, linkApply, deleteLink] : [linkInfo, linkApply], { class: "buttonSettings-dropdown" }) as MenuItem; - return linkDropdown; - } + // // 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(); + // // } + // // }; + // // // this.tooltip.appendChild(this.linkEditor); + // } + // async getTextLinkTargetTitle() { + // let node = this.view.state.selection.$from.nodeAfter; + // let link = node && node.marks.find(m => m.type.name === "link"); + // if (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) { + // let 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; + // if (currentDoc && anchor1 && anchor2) { + // if (Doc.AreProtosEqual(currentDoc, anchor1)) return StrCast(anchor2.title); + // if (Doc.AreProtosEqual(currentDoc, anchor2)) return StrCast(anchor2.title); + // } + // } + // } + // } else { + // return href; + // } + // } + // } + // } dragElement(elmnt: HTMLElement) { var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss index 29e8b14a8..f92ccf9f5 100644 --- a/src/client/views/nodes/FormattedTextBox.scss +++ b/src/client/views/nodes/FormattedTextBox.scss @@ -46,17 +46,17 @@ height: 100%; } -.menuicon { - display: inline-block; - border-right: 1px solid rgba(0, 0, 0, 0.2); - color: #888; - line-height: 1; - padding: 0 7px; - margin: 1px; - cursor: pointer; - text-align: center; - min-width: 1.4em; -} +// .menuicon { +// display: inline-block; +// border-right: 1px solid rgba(0, 0, 0, 0.2); +// color: #888; +// line-height: 1; +// padding: 0 7px; +// margin: 1px; +// cursor: pointer; +// text-align: center; +// min-width: 1.4em; +// } .strong, .heading { -- cgit v1.2.3-70-g09d2 From 3a5e787e3a3c42ec3208f9f75bd8bfbac71aeb81 Mon Sep 17 00:00:00 2001 From: Fawn Date: Thu, 10 Oct 2019 11:44:11 -0400 Subject: added dropdowns to link tool and brush tool --- src/client/util/TooltipTextMenu.tsx | 315 +++++++++++++++++++++++++----------- 1 file changed, 221 insertions(+), 94 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index d0bb753ba..ce0237527 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -50,6 +50,7 @@ export class TooltipTextMenu { private linkDrag?: HTMLImageElement; private _linkDropdownDom?: Node; private _brushdom?: Node; + private _brushDropdownDom?: Node; private fontSizeDom?: Node; private fontStyleDom?: Node; @@ -154,10 +155,9 @@ export class TooltipTextMenu { // link menu this.updateLinkMenu(); - // let dropdown = await this.createLinkDropdown(); - // let newLinkDropdowndom = dropdown.render(this.view).dom; - // this._linkDropdownDom && this.tooltip.replaceChild(newLinkDropdowndom, this._linkDropdownDom); - // this._linkDropdownDom = newLinkDropdowndom; + let dropdown = await this.createLinkDropdown(); + this._linkDropdownDom = dropdown.render(this.view).dom; + this.tooltip.appendChild(this._linkDropdownDom); // list of font styles this.initFontStyles(); @@ -171,18 +171,18 @@ export class TooltipTextMenu { // 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); + this._brushDropdownDom = this.createBrushDropdown().render(this.view).dom; + this.tooltip.appendChild(this._brushDropdownDom); - // star and pin - this.tooltip.appendChild(this.createLink().render(this.view).dom); + // star + // this.tooltip.appendChild(this.createLink().render(this.view).dom); this.tooltip.appendChild(this.createStar().render(this.view).dom); // this.updateListItemDropdown(":", this.listTypeBtnDom); // - this.updateFromDash(view, undefined, undefined); + await this.updateFromDash(view, undefined, undefined); // TooltipTextMenu.Toolbar = this.wrapper; // dragger @@ -302,11 +302,11 @@ export class TooltipTextMenu { if (!this.linkEditor || !this.linkText) { this.linkEditor = document.createElement("div"); this.linkEditor.className = "ProseMirror-icon menuicon"; - this.linkEditor.style.color = "black"; + // this.linkEditor.style.color = "black"; this.linkText = document.createElement("div"); - this.linkText.style.cssFloat = "left"; - this.linkText.style.marginRight = "5px"; - this.linkText.style.marginLeft = "5px"; + // this.linkText.style.cssFloat = "left"; + // this.linkText.style.marginRight = "5px"; + // this.linkText.style.marginLeft = "5px"; this.linkText.setAttribute("contenteditable", "true"); this.linkText.style.whiteSpace = "nowrap"; this.linkText.style.width = "150px"; @@ -345,9 +345,10 @@ export class TooltipTextMenu { this.linkDrag.style.width = "15px"; this.linkDrag.style.height = "15px"; this.linkDrag.title = "Drag to create link"; - this.linkDrag.style.color = "black"; - this.linkDrag.style.background = "black"; - this.linkDrag.style.cssFloat = "left"; + this.linkDrag.id = "link-drag"; + // this.linkDrag.style.color = "black"; + // this.linkDrag.style.background = "black"; + // this.linkDrag.style.cssFloat = "left"; this.linkDrag.onpointerdown = (e: PointerEvent) => { if (!this.editorProps) return; let dragData = new DragManager.LinkDragData(this.editorProps.Document); @@ -366,7 +367,7 @@ export class TooltipTextMenu { if (proto && docView) { proto.sourceContext = docView.props.ContainingCollectionDoc; } - let text = this.makeLink(linkDoc, ctrlKey ? "onRight" : "inTab"); + let text = this.makeLinkToDoc(linkDoc, 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 } @@ -396,81 +397,125 @@ export class TooltipTextMenu { // this.tooltip.appendChild(this.linkEditor); } - // updateLinkMenu() { - // if (!this.linkEditor || !this.linkText) { - // this.linkEditor = document.createElement("div"); - // this.linkEditor.className = "ProseMirror-icon menuicon"; - // this.linkDrag = document.createElement("img"); - // this.linkDrag.src = "https://seogurusnyc.com/wp-content/uploads/2016/12/link-1.png"; - // this.linkDrag.style.width = "13px"; - // this.linkDrag.style.height = "13px"; - // this.linkDrag.title = "Drag to create link"; - // this.linkDrag.id = "link-drag"; - // this.linkDrag.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) => { - // 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.linkEditor.appendChild(this.linkDrag); - // this.tooltip.appendChild(this.linkEditor); - // } + async getTextLinkTargetTitle() { + let node = this.view.state.selection.$from.nodeAfter; + let link = node && node.marks.find(m => m.type.name === "link"); + // let href = link!.attrs.href; + if (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) { + let 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; + if (currentDoc && anchor1 && anchor2) { + if (Doc.AreProtosEqual(currentDoc, anchor1)) { + return StrCast(anchor2.title); + } + if (Doc.AreProtosEqual(currentDoc, anchor2)) { + return StrCast(anchor1.title); + } + } + } + } + } else { + return href; + } + } else { + return link.attrs.title; + } + } + } - // // 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(); - // // } - // // }; - // // // this.tooltip.appendChild(this.linkEditor); - // } + async createLinkDropdown() { + let targetTitle = await this.getTextLinkTargetTitle(); + let 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({ + title: "", + execEvent: "", + class: "button-setting-disabled", + css: "", + render() { + let p = document.createElement("p"); + p.textContent = "Linked to:"; + + input.type = "text"; + input.placeholder = "Enter URL"; + console.log(targetTitle); + if (targetTitle) input.value = targetTitle; + input.onclick = (e: MouseEvent) => { + input.select(); + input.focus(); + }; + + let div = document.createElement("div"); + div.appendChild(p); + div.appendChild(input); + return div; + }, + enable() { return false; }, + run(p1, p2, p3, event) { + event.stopPropagation(); + } + }); + + // menu item to update/apply the hyperlink to the selected text + let linkApply = new MenuItem({ + title: "", + execEvent: "", + class: "", + css: "", + render() { + let button = document.createElement("button"); + button.className = "link-url-button"; + button.textContent = "Apply hyperlink"; + return button; + }, + enable() { return false; }, + run: (state, dispatch, view, event) => { + event.stopPropagation(); + this.makeLinkToURL(input.value, "onRight"); + } + }); + + // menu item to remove the link + // TODO: allow this to be undoable + let self = this; + let deleteLink = new MenuItem({ + title: "Delete link", + execEvent: "", + class: "separated-button", + css: "", + render() { + let button = document.createElement("button"); + button.textContent = "Remove link"; + + let 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; + } + }); - // async getTextLinkTargetTitle() { - // let node = this.view.state.selection.$from.nodeAfter; - // let link = node && node.marks.find(m => m.type.name === "link"); - // if (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) { - // let 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; - // if (currentDoc && anchor1 && anchor2) { - // if (Doc.AreProtosEqual(currentDoc, anchor1)) return StrCast(anchor2.title); - // if (Doc.AreProtosEqual(currentDoc, anchor2)) return StrCast(anchor2.title); - // } - // } - // } - // } else { - // return href; - // } - // } - // } - // } + + let linkDropdown = new Dropdown(targetTitle ? [linkInfo, linkApply, deleteLink] : [linkInfo, linkApply], { class: "buttonSettings-dropdown" }) as MenuItem; + return linkDropdown; + } dragElement(elmnt: HTMLElement) { var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; @@ -532,7 +577,7 @@ export class TooltipTextMenu { // let link = state.schema.mark(state.schema.marks.link, { href: target, location: location }); // } - makeLink = (targetDoc: Doc, location: string): string => { + makeLinkToDoc = (targetDoc: Doc, location: string): string => { let target = Utils.prepend("/doc/" + targetDoc[Id]); 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, guid: targetDoc[Id] }); @@ -548,6 +593,15 @@ export class TooltipTextMenu { 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.name === "link"); @@ -711,7 +765,7 @@ export class TooltipTextMenu { label: "Summarize", icon: icons.join, css: "color:white;", - class: "summarize", + class: "menuicon", execEvent: "", run: (state, dispatch) => { TooltipTextMenu.insertStar(this.view.state, this.view.dispatch); @@ -743,15 +797,21 @@ export class TooltipTextMenu { 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; return new MenuItem({ title: "Brush tool", label: "Brush tool", icon: icon, css: "color:white;", - class: active ? "brush-active" : "brush", + 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; + self._brushDropdownDom && self.tooltip.replaceChild(newBrushDropdowndom, self._brushDropdownDom); + self._brushDropdownDom = newBrushDropdowndom; }, active: (state) => { return true; @@ -798,6 +858,67 @@ 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 += ", "; + }); + label = label.substring(0, label.length - 2); + } else { + label = "No marks are currently stored"; + } + + + let brushInfo = new MenuItem({ + title: "", + label: label, + execEvent: "", + class: "button-setting-disabled", + css: "", + enable() { return false; }, + run(p1, p2, p3, event) { + event.stopPropagation(); + } + }); + + let self = this; + let clearBrush = new MenuItem({ + title: "Clear brush", + execEvent: "", + class: "separated-button", + css: "", + render() { + let button = document.createElement("button"); + button.textContent = "Clear brush"; + + let wrapper = document.createElement("div"); + wrapper.appendChild(button); + return wrapper; + }, + enable() { return true; }, + run() { + TooltipTextMenuManager.Instance._brushIsEmpty = true; + TooltipTextMenuManager.Instance._brushMarks = new Set(); + + // update brush tool + // TODO: this probably isn't very clean + let newBrushdom = self.createBrush().render(self.view).dom; + self._brushdom && self.tooltip.replaceChild(newBrushdom, self._brushdom); + self._brushdom = newBrushdom; + let 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; + } + // createCollapse() { // this._collapseBtn = new MenuItem({ // title: "Collapse", @@ -978,7 +1099,7 @@ export class TooltipTextMenu { update(view: EditorView, lastState: EditorState | undefined) { this.updateFromDash(view, lastState, this.editorProps); } //updates the tooltip menu when the selection changes - public updateFromDash(view: EditorView, lastState: EditorState | undefined, props: any) { + public async updateFromDash(view: EditorView, lastState: EditorState | undefined, props: any) { if (!view) { console.log("no editor? why?"); return; @@ -1000,6 +1121,12 @@ export class TooltipTextMenu { } //UPDATE LIST ITEM DROPDOWN + // update link dropdown + let dropdown = await this.createLinkDropdown(); + let newLinkDropdowndom = dropdown.render(this.view).dom; + this._linkDropdownDom && this.tooltip.replaceChild(newLinkDropdowndom, this._linkDropdownDom); + this._linkDropdownDom = newLinkDropdowndom; + //UPDATE FONT STYLE DROPDOWN let activeStyles = this.activeMarksOnSelection(this.fontStyles); if (activeStyles !== undefined) { -- cgit v1.2.3-70-g09d2 From 1b0f0e5e434bc23128b50303ffc2b13244ba6325 Mon Sep 17 00:00:00 2001 From: Fawn Date: Thu, 10 Oct 2019 12:01:35 -0400 Subject: started colors on rich text editor --- src/client/util/TooltipTextMenu.tsx | 100 ++++++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 10 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index ce0237527..9dd8e6688 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -21,6 +21,7 @@ import { Cast, NumCast, StrCast } from '../../new_fields/Types'; import { updateBullets } from './ProsemirrorExampleTransfer'; import { DocumentDecorations } from '../views/DocumentDecorations'; import { SelectionManager } from './SelectionManager'; +import { PastelSchemaPalette } from '../../new_fields/SchemaHeaderField'; const { toggleMark, setBlockType } = require("prosemirror-commands"); const { openPrompt, TextField } = require("./ProsemirrorCopy/prompt.js"); @@ -623,16 +624,6 @@ export class TooltipTextMenu { } - public static insertStar(state: EditorState, 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; - } //will display a remove-list-type button if selection is in list, otherwise will show list type dropdown updateListItemDropdown(label: string, listTypeBtn: any) { @@ -774,6 +765,93 @@ export class TooltipTextMenu { }); } + public static insertStar(state: EditorState, 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; + } + + createColorTool() { + 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); + } + + }); + } + + insertColor(color: String, state: EditorState, dispatch: any) { + + } + + createColorDropdown() { + // menu item for color picker + let colors = new MenuItem({ + title: "", + execEvent: "", + class: "button-setting-disabled", + css: "", + render() { + let p = document.createElement("p"); + p.textContent = "Change color:"; + + // input.type = "text"; + // input.placeholder = "Enter URL"; + // console.log(targetTitle); + // if (targetTitle) input.value = targetTitle; + // input.onclick = (e: MouseEvent) => { + // input.select(); + // input.focus(); + // }; + + let colorsWrapper = document.createElement("div"); + colorsWrapper.className = "colorPicker-wrapper"; + + let colors = [ + PastelSchemaPalette.get("pink2"), + PastelSchemaPalette.get("purple4"), + PastelSchemaPalette.get("bluegreen1"), + PastelSchemaPalette.get("yellow4"), + PastelSchemaPalette.get("red2"), + PastelSchemaPalette.get("bluegreen7"), + PastelSchemaPalette.get("bluegreen5"), + PastelSchemaPalette.get("orange1"), + "#f1efeb", + "#000" + ]; + + colors.forEach(color => { + let button = document.createElement("button"); + button.className = color === TooltipTextMenuManager.Instance.color ? "colorPicker active" : "colorPicker"; + if (color) button.style.backgroundColor = color; + + }); + + let div = document.createElement("div"); + div.appendChild(p); + return div; + }, + enable() { return false; }, + run(p1, p2, p3, event) { + event.stopPropagation(); + } + }); + + let colorDropdown = new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem; + return colorDropdown; + } + deleteLinkItem() { const icon = { height: 16, width: 16, @@ -1307,6 +1385,8 @@ class TooltipTextMenuManager { public _brushMarks: Set | undefined; public _brushIsEmpty: boolean = true; + public color: String = "#000"; + public activeMenu: TooltipTextMenu | undefined; static get Instance() { -- cgit v1.2.3-70-g09d2 From e8095539c208169b1883e8e73686382d8d8c6336 Mon Sep 17 00:00:00 2001 From: Fawn Date: Sat, 12 Oct 2019 17:18:58 -0400 Subject: created color picker for text on rich text menu --- src/client/util/RichTextSchema.tsx | 18 +++++++++ src/client/util/TooltipTextMenu.scss | 21 +++++++++++ src/client/util/TooltipTextMenu.tsx | 73 ++++++++++++++---------------------- 3 files changed, 67 insertions(+), 45 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index a5502577b..e943b71e8 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -308,6 +308,24 @@ export const marks: { [index: string]: MarkSpec } = { } }, + // :: MarkSpec Coloring on text. Has `color` attribute that defined the color of the marked text. + color: { + attrs: { + color: { default: "#000" } + }, + inclusive: false, + parseDOM: [{ + tag: "span", getAttrs(dom: any) { + return { color: dom.getAttribute("color") }; + } + }], + toDOM(node: any) { + return node.attrs.color ? ['span', { style: 'color:' + node.attrs.color }] : ['span', { style: 'color: black' }]; + // ["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, class: "prosemirror-attribution" }, node.attrs.title], ["br"]] : + // ["a", { ...node.attrs }, 0]; + } + }, + // :: MarkSpec An emphasis mark. Rendered as an `` element. // Has parse rules that also match `` and `font-style: italic`. em: { diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss index 11c937471..f94799936 100644 --- a/src/client/util/TooltipTextMenu.scss +++ b/src/client/util/TooltipTextMenu.scss @@ -336,6 +336,27 @@ } } +.colorPicker-wrapper { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + margin-top: 3px; + margin-left: -3px; + width: calc(100% + 6px); +} + +button.colorPicker { + width: 20px; + height: 20px; + border-radius: 15px !important; + margin: 3px; + border: none !important; + + &.active { + border: 2px solid white !important; + } +} + // @import "../views/globalCssVariables"; // .ProseMirror-textblock-dropdown { diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 9dd8e6688..32a910497 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -28,9 +28,11 @@ const { openPrompt, TextField } = require("./ProsemirrorCopy/prompt.js"); //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 private view: EditorView; - private editorProps: FieldViewProps & FormattedTextBoxProps | undefined; + // private editorProps: FieldViewProps & FormattedTextBoxProps | undefined; private fontStyles: MarkType[] = []; private fontSizes: MarkType[] = []; private listTypes: (NodeType | any)[] = []; @@ -39,40 +41,38 @@ export class TooltipTextMenu { private listTypeToIcon: Map = new Map(); private _activeMarks: Mark[] = []; private _marksToDoms: Map = new Map(); - // private _brushMarks?: Set; + private _collapsed: boolean = false; //private link: HTMLAnchorElement; // editor doms public tooltip: HTMLElement = document.createElement("div"); private wrapper: HTMLDivElement = document.createElement("div"); + // editor button doms private linkEditor?: HTMLDivElement; private linkText?: HTMLDivElement; private linkDrag?: HTMLImageElement; private _linkDropdownDom?: Node; private _brushdom?: Node; private _brushDropdownDom?: Node; - private fontSizeDom?: Node; private fontStyleDom?: Node; private listTypeBtnDom?: Node; - private _collapsed: boolean = false; + // private _collapseBtn?: MenuItem; // private _brushIsEmpty: boolean = true; - public static Toolbar: HTMLDivElement | undefined; - constructor(view: EditorView) { this.view = view; - // replace old active menu with this - if (TooltipTextMenuManager.Instance.activeMenu) { - TooltipTextMenuManager.Instance.activeMenu.wrapper.remove(); - } - TooltipTextMenuManager.Instance.activeMenu = this; + // // replace old active menu with this + // if (TooltipTextMenuManager.Instance.activeMenu) { + // TooltipTextMenuManager.Instance.activeMenu.wrapper.remove(); + // } + // TooltipTextMenuManager.Instance.activeMenu = this; // initialize the tooltip this.createTooltip(view); @@ -84,30 +84,10 @@ export class TooltipTextMenu { // positioning? TooltipTextMenu.Toolbar = this.wrapper; - - // // position wrapper - // if (TooltipTextMenuManager.Instance.isPinned) { - // this.wrapper.style.position = "absolute"; - // this.wrapper.style.left = TooltipTextMenuManager.Instance.pinnedX + "px"; - // this.wrapper.style.top = TooltipTextMenuManager.Instance.pinnedY + "px"; - - // // if pinned, append to mainview - // const mainView = document.querySelector("#main-div"); - // mainView && mainView.appendChild(this.wrapper); - // } else { - // this.wrapper.style.position = "absolute"; - // this.wrapper.style.top = (this.wrapper.offsetTop + TooltipTextMenuManager.Instance.unpinnedY) + "px"; - // this.wrapper.style.left = (this.wrapper.offsetLeft + TooltipTextMenuManager.Instance.unpinnedX) + "px"; - - // // add tooltip to outerdiv to circumvent scaling problem - // const outer_div = this.editorProps.outer_div; - // outer_div && outer_div(this.wrapper); - // } - } private async createTooltip(view: EditorView) { - // initialize element + // initialize tooltip dom this.tooltip = document.createElement("div"); this.tooltip.className = "tooltipMenu"; @@ -154,6 +134,10 @@ export class TooltipTextMenu { }); + // color menu + this.tooltip.appendChild(this.createColorTool().render(this.view).dom); + this.tooltip.appendChild(this.createColorDropdown().render(this.view).dom); + // link menu this.updateLinkMenu(); let dropdown = await this.createLinkDropdown(); @@ -785,18 +769,22 @@ export class TooltipTextMenu { class: "menuicon", execEvent: "", run: (state, dispatch) => { - TooltipTextMenu.insertStar(this.view.state, this.view.dispatch); + TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, this.view.state, this.view.dispatch); } }); } - insertColor(color: String, state: EditorState, dispatch: any) { + public static insertColor(color: String, state: EditorState, 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)); } createColorDropdown() { // menu item for color picker + let self = this; let colors = new MenuItem({ title: "", execEvent: "", @@ -806,15 +794,6 @@ export class TooltipTextMenu { let p = document.createElement("p"); p.textContent = "Change color:"; - // input.type = "text"; - // input.placeholder = "Enter URL"; - // console.log(targetTitle); - // if (targetTitle) input.value = targetTitle; - // input.onclick = (e: MouseEvent) => { - // input.select(); - // input.focus(); - // }; - let colorsWrapper = document.createElement("div"); colorsWrapper.className = "colorPicker-wrapper"; @@ -834,12 +813,16 @@ export class TooltipTextMenu { colors.forEach(color => { let button = document.createElement("button"); button.className = color === TooltipTextMenuManager.Instance.color ? "colorPicker active" : "colorPicker"; - if (color) button.style.backgroundColor = color; - + if (color) { + button.style.backgroundColor = color; + button.onclick = e => TooltipTextMenuManager.Instance.color = color; + } + colorsWrapper.appendChild(button); }); let div = document.createElement("div"); div.appendChild(p); + div.appendChild(colorsWrapper); return div; }, enable() { return false; }, -- cgit v1.2.3-70-g09d2 From 43b09cb2aded66e67006d99e544d90637a62c048 Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 23 Oct 2019 21:54:10 -0400 Subject: improved styling and colors of color picker on richtexteditor --- src/client/util/TooltipTextMenu.scss | 18 +++++++++ src/client/util/TooltipTextMenu.tsx | 75 +++++++++++++++++++++++++++--------- src/new_fields/SchemaHeaderField.ts | 11 ++++++ 3 files changed, 85 insertions(+), 19 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss index f94799936..794998a01 100644 --- a/src/client/util/TooltipTextMenu.scss +++ b/src/client/util/TooltipTextMenu.scss @@ -182,6 +182,24 @@ } } +#colorPicker { + position: relative; + + svg { + width: 18px; + height: 18px; + } + + .buttonColor { + position: absolute; + top: 24px; + left: 1px; + width: 24px; + height: 4px; + margin-top: 0; + } +} + .strong, .heading { font-weight: bold; } .em { font-style: italic; } .underline {text-decoration: underline} diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 32a910497..92765b1e0 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -21,7 +21,7 @@ import { Cast, NumCast, StrCast } from '../../new_fields/Types'; import { updateBullets } from './ProsemirrorExampleTransfer'; import { DocumentDecorations } from '../views/DocumentDecorations'; import { SelectionManager } from './SelectionManager'; -import { PastelSchemaPalette } from '../../new_fields/SchemaHeaderField'; +import { PastelSchemaPalette, DarkPastelSchemaPalette } from '../../new_fields/SchemaHeaderField'; const { toggleMark, setBlockType } = require("prosemirror-commands"); const { openPrompt, TextField } = require("./ProsemirrorCopy/prompt.js"); @@ -58,6 +58,8 @@ export class TooltipTextMenu { private fontSizeDom?: Node; private fontStyleDom?: Node; private listTypeBtnDom?: Node; + private colorDom?: Node; + private colorDropdownDom?: Node; // private _collapseBtn?: MenuItem; @@ -99,7 +101,7 @@ export class TooltipTextMenu { { command: toggleMark(schema.marks.strikethrough), dom: this.icon("S", "strikethrough", "Strikethrough") }, { command: toggleMark(schema.marks.superscript), dom: this.icon("s", "superscript", "Superscript") }, { command: toggleMark(schema.marks.subscript), dom: this.icon("s", "subscript", "Subscript") }, - { command: toggleMark(schema.marks.highlight), dom: this.icon("H", 'blue', 'Blue') } + // { command: toggleMark(schema.marks.highlight), dom: this.icon("H", 'blue', 'Blue') } ]; // add menu items @@ -135,8 +137,10 @@ export class TooltipTextMenu { }); // color menu - this.tooltip.appendChild(this.createColorTool().render(this.view).dom); - this.tooltip.appendChild(this.createColorDropdown().render(this.view).dom); + this.colorDom = this.createColorTool().render(this.view).dom; + this.colorDropdownDom = this.createColorDropdown().render(this.view).dom; + this.tooltip.appendChild(this.colorDom); + this.tooltip.appendChild(this.colorDropdownDom); // link menu this.updateLinkMenu(); @@ -760,14 +764,35 @@ export class TooltipTextMenu { return true; } + createHighlightTool() { + + } + createColorTool() { return new MenuItem({ - title: "Summarize", - label: "Summarize", - icon: icons.join, + title: "Color", + // label: "Color", + // icon: icon, css: "color:white;", class: "menuicon", execEvent: "", + render() { + 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", "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"; + color.style.backgroundColor = TooltipTextMenuManager.Instance.color.toString(); + + let wrapper = document.createElement("div"); + wrapper.id = "colorPicker"; + wrapper.appendChild(svg); + wrapper.appendChild(color); + return wrapper; + }, run: (state, dispatch) => { TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, this.view.state, this.view.dispatch); } @@ -798,15 +823,15 @@ export class TooltipTextMenu { colorsWrapper.className = "colorPicker-wrapper"; let colors = [ - PastelSchemaPalette.get("pink2"), - PastelSchemaPalette.get("purple4"), - PastelSchemaPalette.get("bluegreen1"), - PastelSchemaPalette.get("yellow4"), - PastelSchemaPalette.get("red2"), - PastelSchemaPalette.get("bluegreen7"), - PastelSchemaPalette.get("bluegreen5"), - PastelSchemaPalette.get("orange1"), - "#f1efeb", + DarkPastelSchemaPalette.get("pink2"), + DarkPastelSchemaPalette.get("purple4"), + DarkPastelSchemaPalette.get("bluegreen1"), + DarkPastelSchemaPalette.get("yellow4"), + DarkPastelSchemaPalette.get("red2"), + DarkPastelSchemaPalette.get("bluegreen7"), + DarkPastelSchemaPalette.get("bluegreen5"), + DarkPastelSchemaPalette.get("orange1"), + "#757472", "#000" ]; @@ -815,7 +840,19 @@ export class TooltipTextMenu { button.className = color === TooltipTextMenuManager.Instance.color ? "colorPicker active" : "colorPicker"; if (color) { button.style.backgroundColor = color; - button.onclick = e => TooltipTextMenuManager.Instance.color = color; + button.onclick = e => { + TooltipTextMenuManager.Instance.color = color; + + 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; + self.colorDom && self.tooltip.replaceChild(colorDom, self.colorDom); + self.colorDropdownDom && self.tooltip.replaceChild(colorDropdownDom, self.colorDropdownDom); + self.colorDom = colorDom; + self.colorDropdownDom = colorDropdownDom; + }; } colorsWrapper.appendChild(button); }); @@ -1183,8 +1220,8 @@ export class TooltipTextMenu { //UPDATE LIST ITEM DROPDOWN // update link dropdown - let dropdown = await this.createLinkDropdown(); - let newLinkDropdowndom = dropdown.render(this.view).dom; + let linkDropdown = await this.createLinkDropdown(); + let newLinkDropdowndom = linkDropdown.render(this.view).dom; this._linkDropdownDom && this.tooltip.replaceChild(newLinkDropdowndom, this._linkDropdownDom); this._linkDropdownDom = newLinkDropdowndom; diff --git a/src/new_fields/SchemaHeaderField.ts b/src/new_fields/SchemaHeaderField.ts index 92d0aec9a..42a8485ac 100644 --- a/src/new_fields/SchemaHeaderField.ts +++ b/src/new_fields/SchemaHeaderField.ts @@ -41,6 +41,17 @@ export const PastelSchemaPalette = new Map([ export const RandomPastel = () => Array.from(PastelSchemaPalette.values())[Math.floor(Math.random() * PastelSchemaPalette.size)]; +export const DarkPastelSchemaPalette = new Map([ + ["pink2", "#c932b0"], + ["purple4", "#913ad6"], + ["bluegreen1", "#3978ed"], + ["bluegreen7", "#2adb3e"], + ["bluegreen5", "#21b0eb"], + ["yellow4", "#edcc0c"], + ["red2", "#eb3636"], + ["orange1", "#f2740f"], +]); + @scriptingGlobal @Deserializable("schemaheader") export class SchemaHeaderField extends ObjectField { -- cgit v1.2.3-70-g09d2 From bd817ac118c53845c882b89c4b8a110dc8fe10c2 Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 23 Oct 2019 22:22:34 -0400 Subject: added dropdown to highlighter tool to choose colors --- src/client/util/RichTextSchema.tsx | 17 +++++- src/client/util/TooltipTextMenu.tsx | 113 ++++++++++++++++++++++++++++++++++-- 2 files changed, 124 insertions(+), 6 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index e943b71e8..22756be24 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -321,8 +321,21 @@ export const marks: { [index: string]: MarkSpec } = { }], toDOM(node: any) { return node.attrs.color ? ['span', { style: 'color:' + node.attrs.color }] : ['span', { style: 'color: black' }]; - // ["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, class: "prosemirror-attribution" }, node.attrs.title], ["br"]] : - // ["a", { ...node.attrs }, 0]; + } + }, + + highlight2: { + attrs: { + highlight: { default: "transparent" } + }, + inclusive: false, + parseDOM: [{ + tag: "span", getAttrs(dom: any) { + return { highlight: dom.getAttribute("backgroundColor") }; + } + }], + toDOM(node: any) { + return node.attrs.highlight ? ['span', { style: 'background-color:' + node.attrs.highlight }] : ['span', { style: 'background-color: transparent' }]; } }, diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 92765b1e0..0626eb504 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -1,4 +1,4 @@ -import { library } from '@fortawesome/fontawesome-svg-core'; +import { library, dom } from '@fortawesome/fontawesome-svg-core'; import { faListUl } from '@fortawesome/free-solid-svg-icons'; import { action, observable } from "mobx"; import { Dropdown, icons, MenuItem } from "prosemirror-menu"; //no import css @@ -60,6 +60,8 @@ export class TooltipTextMenu { private listTypeBtnDom?: Node; private colorDom?: Node; private colorDropdownDom?: Node; + private highlightDom?: Node; + private highlightDropdownDom?: Node; // private _collapseBtn?: MenuItem; @@ -136,6 +138,12 @@ export class TooltipTextMenu { }); + // 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); + // color menu this.colorDom = this.createColorTool().render(this.view).dom; this.colorDropdownDom = this.createColorDropdown().render(this.view).dom; @@ -765,14 +773,111 @@ export class TooltipTextMenu { } createHighlightTool() { + return new MenuItem({ + title: "Highlight", + css: "color:white;", + class: "menuicon", + execEvent: "", + render() { + 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", "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() === "transparent" ? "#fff" : TooltipTextMenuManager.Instance.highlight.toString(); + + let wrapper = document.createElement("div"); + wrapper.id = "colorPicker"; + wrapper.appendChild(svg); + wrapper.appendChild(color); + return wrapper; + }, + run: (state, dispatch) => { + TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlight, this.view.state, this.view.dispatch); + } + }); + } + + public static insertHighlight(color: String, state: EditorState, dispatch: any) { + if (state.selection.empty) return false; + + let highlightMark = state.schema.mark(state.schema.marks.highlight2, { highlight: color }); + dispatch(state.tr.addMark(state.selection.from, state.selection.to, highlightMark)); + } + + createHighlightDropdown() { + // menu item for color picker + let self = this; + let colors = new MenuItem({ + title: "", + execEvent: "", + class: "button-setting-disabled", + css: "", + render() { + let p = document.createElement("p"); + p.textContent = "Change highlight:"; + + let colorsWrapper = document.createElement("div"); + colorsWrapper.className = "colorPicker-wrapper"; + + let colors = [ + PastelSchemaPalette.get("pink2"), + PastelSchemaPalette.get("purple4"), + PastelSchemaPalette.get("bluegreen1"), + PastelSchemaPalette.get("yellow4"), + PastelSchemaPalette.get("red2"), + PastelSchemaPalette.get("bluegreen7"), + PastelSchemaPalette.get("bluegreen5"), + PastelSchemaPalette.get("orange1"), + // "#f1f0eb", + "transparent" + ]; + colors.forEach(color => { + let button = document.createElement("button"); + button.className = color === TooltipTextMenuManager.Instance.highlight ? "colorPicker active" : "colorPicker"; + if (color) { + button.style.backgroundColor = color === "transparent" ? "#fff" : color; + button.style.color = "black"; + button.textContent = color === "transparent" ? "X" : ""; + button.onclick = e => { + TooltipTextMenuManager.Instance.highlight = color; + + TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlight, 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; + }; + } + colorsWrapper.appendChild(button); + }); + + let div = document.createElement("div"); + div.appendChild(p); + div.appendChild(colorsWrapper); + return div; + }, + enable() { return false; }, + run(p1, p2, p3, event) { + event.stopPropagation(); + } + }); + + let colorDropdown = new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem; + return colorDropdown; } createColorTool() { return new MenuItem({ title: "Color", - // label: "Color", - // icon: icon, css: "color:white;", class: "menuicon", execEvent: "", @@ -796,7 +901,6 @@ export class TooltipTextMenu { run: (state, dispatch) => { TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, this.view.state, this.view.dispatch); } - }); } @@ -1406,6 +1510,7 @@ class TooltipTextMenuManager { public _brushIsEmpty: boolean = true; public color: String = "#000"; + public highlight: String = "transparent"; public activeMenu: TooltipTextMenu | undefined; -- cgit v1.2.3-70-g09d2 From 66a15a7ac6a6907b398f142554f5d1a66988b630 Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 23 Oct 2019 22:57:02 -0400 Subject: switched out text formatting buttons on rich text editor for fontawesome svg --- src/client/util/TooltipTextMenu.scss | 1 + src/client/util/TooltipTextMenu.tsx | 36 +++++++++++++++++++++++++----------- 2 files changed, 26 insertions(+), 11 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss index 794998a01..16585d20b 100644 --- a/src/client/util/TooltipTextMenu.scss +++ b/src/client/util/TooltipTextMenu.scss @@ -188,6 +188,7 @@ svg { width: 18px; height: 18px; + margin-top: 11px; } .buttonColor { diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 0626eb504..d32889dd0 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -95,14 +95,14 @@ export class TooltipTextMenu { this.tooltip = document.createElement("div"); this.tooltip.className = "tooltipMenu"; - // init buttons to the tooltip + // init buttons to the tooltip -- paths to svgs are obtained from fontawesome let items = [ - { command: toggleMark(schema.marks.strong), dom: this.icon("B", "strong", "Bold") }, - { command: toggleMark(schema.marks.em), dom: this.icon("i", "em", "Italic") }, - { command: toggleMark(schema.marks.underline), dom: this.icon("U", "underline", "Underline") }, - { command: toggleMark(schema.marks.strikethrough), dom: this.icon("S", "strikethrough", "Strikethrough") }, - { command: toggleMark(schema.marks.superscript), dom: this.icon("s", "superscript", "Superscript") }, - { command: toggleMark(schema.marks.subscript), dom: this.icon("s", "subscript", "Subscript") }, + { command: toggleMark(schema.marks.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') } ]; @@ -787,7 +787,7 @@ export class TooltipTextMenu { let color = document.createElement("div"); color.className = "buttonColor"; - color.style.backgroundColor = TooltipTextMenuManager.Instance.highlight.toString() === "transparent" ? "#fff" : TooltipTextMenuManager.Instance.highlight.toString(); + color.style.backgroundColor = TooltipTextMenuManager.Instance.highlight.toString(); let wrapper = document.createElement("div"); wrapper.id = "colorPicker"; @@ -832,7 +832,7 @@ export class TooltipTextMenu { PastelSchemaPalette.get("bluegreen7"), PastelSchemaPalette.get("bluegreen5"), PastelSchemaPalette.get("orange1"), - // "#f1f0eb", + "white", "transparent" ]; @@ -840,8 +840,7 @@ export class TooltipTextMenu { let button = document.createElement("button"); button.className = color === TooltipTextMenuManager.Instance.highlight ? "colorPicker active" : "colorPicker"; if (color) { - button.style.backgroundColor = color === "transparent" ? "#fff" : color; - button.style.color = "black"; + button.style.backgroundColor = color; button.textContent = color === "transparent" ? "X" : ""; button.onclick = e => { TooltipTextMenuManager.Instance.highlight = color; @@ -1239,6 +1238,21 @@ export class TooltipTextMenu { 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>) { let $from = state.selection.$from; -- cgit v1.2.3-70-g09d2 From e3f06e390f98cc5b97d63fc287daff994d5fef6f Mon Sep 17 00:00:00 2001 From: Fawn Date: Thu, 14 Nov 2019 23:01:49 -0500 Subject: double cliking dragger collapses menu --- src/client/util/RichTextSchema.tsx | 1 + src/client/util/TooltipTextMenu.scss | 399 ++--------------------- src/client/util/TooltipTextMenu.tsx | 610 +++++++++++++++-------------------- 3 files changed, 278 insertions(+), 732 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 1eea529d2..69f9a4c53 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -130,6 +130,7 @@ export const nodes: { [index: string]: NodeSpec } = { // } // }] }, + // :: NodeSpec An inline image (``) node. Supports `src`, // `alt`, and `href` attributes. The latter two default to the empty // string. diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss index 1f619b4f5..8310a0da6 100644 --- a/src/client/util/TooltipTextMenu.scss +++ b/src/client/util/TooltipTextMenu.scss @@ -1,6 +1,5 @@ @import "../views/globalCssVariables"; .ProseMirror-menu-dropdown-wrap { - // margin: 0 4px; display: inline-block; position: relative; } @@ -39,9 +38,10 @@ font-size: 12px; background: white; border: 1px solid rgb(223, 223, 223); - min-width: 50px; + min-width: 40px; z-index: 50000; position: absolute; + box-sizing: content-box; .ProseMirror-menu-dropdown-item { cursor: pointer; @@ -91,20 +91,22 @@ .wrapper { position: absolute; pointer-events: all; -} + display: flex; + align-items: center; + transform: translateY(-85px); -.tooltipMenu { - position: absolute; - z-index: 20000; + height: 35px; background: #323232; border-radius: 6px; - transform: translateY(-85px); + box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25); + +} + +.tooltipMenu, .basic-tools { + z-index: 20000; pointer-events: all; - height: 35px; - // width: 650px; padding: 3px; padding-bottom: 5px; - box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25); display: flex; align-items: center; @@ -203,7 +205,7 @@ } .underline svg { - margin-top: 15px; + margin-top: 13px; } .font-size-indicator { @@ -232,21 +234,19 @@ width: 1em; height: 1em; stroke-width: 3; - // stroke: greenyellow; fill: greenyellow; margin-right: 15px; } -.dragger { +.dragger-wrapper { color: #eee; - margin-left: 5px; - width: 16px; height: 22px; - display: inline-block; + padding: 0 5px; + box-sizing: content-box; cursor: grab; - .dragger-wrapper { - width: 100%; + .dragger { + width: 18px; height: 100%; display: flex; justify-content: space-evenly; @@ -259,11 +259,6 @@ } } -// .menuicon:hover + .ProseMirror-menu-dropdown-wrap .buttonSettings-dropdown, -// .menuicon-active:hover + .ProseMirror-menu-dropdown-wrap .buttonSettings-dropdown { -// background-color: black; -// } - .button-dropdown-wrapper { display: flex; align-content: center; @@ -374,360 +369,4 @@ button.colorPicker { &.active { border: 2px solid white !important; } -} - -// @import "../views/globalCssVariables"; - -// .ProseMirror-textblock-dropdown { -// min-width: 3em; -// } - -// .ProseMirror-menu { -// margin: 0 -4px; -// line-height: 1; -// } - -// .ProseMirror-tooltip .ProseMirror-menu { -// width: -webkit-fit-content; -// width: fit-content; -// white-space: pre; -// } - -// .ProseMirror-menuitem { -// margin-right: 3px; -// display: inline-block; -// z-index: 50000; -// position: relative; -// } - -// .ProseMirror-menuseparator { -// // border-right: 1px solid #ddd; -// margin-right: 3px; -// } - -// .ProseMirror-menu-dropdown, .ProseMirror-menu-dropdown-menu { -// font-size: 90%; -// white-space: nowrap; -// } - -// .ProseMirror-menu-dropdown { -// vertical-align: 1px; -// cursor: pointer; -// position: relative; -// padding-right: 15px; -// margin: 3px; -// background: white; -// border-radius: 3px; -// text-align: center; -// } - -// .ProseMirror-menu-dropdown-wrap { -// padding: 1px 0 1px 4px; -// display: inline-block; -// position: relative; -// } - -// .ProseMirror-menu-dropdown:after { -// content: ""; -// border-left: 4px solid transparent; -// border-right: 4px solid transparent; -// border-top: 4px solid currentColor; -// opacity: .6; -// position: absolute; -// right: 4px; -// top: calc(50% - 2px); -// } - -// .ProseMirror-menu-dropdown-menu, .ProseMirror-menu-submenu { -// background: $dark-color; -// color:white; -// border: 1px solid rgb(223, 223, 223); -// padding: 2px; -// } - -// .ProseMirror-menu-dropdown-menu { -// z-index: 50000; -// min-width: 6em; -// background: white; -// position: absolute; -// } - -// .linking { -// text-align: center; -// } - -// .ProseMirror-menu-dropdown-item { -// cursor: pointer; -// padding: 2px 8px 2px 4px; -// width: auto; -// z-index: 100000; -// } - -// .ProseMirror-menu-dropdown-item:hover { -// background: white; -// } - -// .ProseMirror-menu-submenu-wrap { -// position: relative; -// margin-right: -4px; -// } - -// .ProseMirror-menu-submenu-label:after { -// content: ""; -// border-top: 4px solid transparent; -// border-bottom: 4px solid transparent; -// border-left: 4px solid currentColor; -// opacity: .6; -// position: absolute; -// right: 4px; -// top: calc(50% - 4px); -// } - -// .ProseMirror-menu-submenu { -// display: none; -// min-width: 4em; -// left: 100%; -// top: -3px; -// } - -// .ProseMirror-menu-active { -// background: #eee; -// border-radius: 4px; -// } - -// .ProseMirror-menu-active { -// background: #eee; -// border-radius: 4px; -// } - -// .ProseMirror-menu-disabled { -// opacity: .3; -// } - -// .ProseMirror-menu-submenu-wrap:hover .ProseMirror-menu-submenu, .ProseMirror-menu-submenu-wrap-active .ProseMirror-menu-submenu { -// display: block; -// } - -// .ProseMirror-menubar { -// border-top-left-radius: inherit; -// border-top-right-radius: inherit; -// position: relative; -// min-height: 1em; -// color: white; -// padding: 10px 10px; -// top: 0; left: 0; right: 0; -// border-bottom: 1px solid silver; -// background:$dark-color; -// z-index: 10; -// -moz-box-sizing: border-box; -// box-sizing: border-box; -// overflow: visible; -// } - -// .ProseMirror-icon { -// display: inline-block; -// line-height: .8; -// vertical-align: -2px; /* Compensate for padding */ -// padding: 2px 8px; -// cursor: pointer; -// } - -// .ProseMirror-menu-disabled.ProseMirror-icon { -// cursor: default; -// } - -// .ProseMirror-icon svg { -// fill:white; -// height: 1em; -// } - -// .ProseMirror-icon span { -// vertical-align: text-top; -// } - -// .ProseMirror ul, .ProseMirror ol { -// padding-left: 30px; -// } - -// .ProseMirror blockquote { -// padding-left: 1em; -// border-left: 3px solid #eee; -// margin-left: 0; margin-right: 0; -// } - -// .ProseMirror-example-setup-style img { -// cursor: default; -// } - -// .ProseMirror-prompt { -// background: white; -// padding: 5px 10px 5px 15px; -// border: 1px solid silver; -// position: fixed; -// border-radius: 3px; -// z-index: 11; -// box-shadow: -.5px 2px 5px white(255, 255, 255, 0.2); -// } - -// .ProseMirror-prompt h5 { -// margin: 0; -// font-weight: normal; -// font-size: 100%; -// color: #444; -// } - -// .ProseMirror-prompt input[type="text"], -// .ProseMirror-prompt textarea { -// background: white; -// border: none; -// outline: none; -// } - -// .ProseMirror-prompt input[type="text"] { -// padding: 0 4px; -// } - -// .ProseMirror-prompt-close { -// position: absolute; -// left: 2px; top: 1px; -// color: #666; -// border: none; background: transparent; padding: 0; -// } - -// .ProseMirror-prompt-close:after { -// content: "✕"; -// font-size: 12px; -// } - -// .ProseMirror-invalid { -// background: #ffc; -// border: 1px solid #cc7; -// border-radius: 4px; -// padding: 5px 10px; -// position: absolute; -// min-width: 10em; -// } - -// .ProseMirror-prompt-buttons { -// margin-top: 5px; -// display: none; -// } - -// .tooltipMenu { -// position: absolute; -// z-index: 20000; -// background: #121721; -// border: 1px solid silver; -// border-radius: 15px; -// //height: 60px; -// //padding: 2px 10px; -// //margin-top: 100px; -// //-webkit-transform: translateX(-50%); -// //transform: translateX(-50%); -// transform: translateY(-85px); -// pointer-events: all; -// height: fit-content; -// width:550px; -// .ProseMirror-example-setup-style hr { -// padding: 2px 10px; -// border: none; -// margin: 1em 0; -// } - -// .ProseMirror-example-setup-style hr:after { -// content: ""; -// display: block; -// height: 1px; -// background-color: silver; -// line-height: 2px; -// } -// } - -// .tooltipExtras { -// position: absolute; -// z-index: 20000; -// background: #121721; -// border: 1px solid silver; -// border-radius: 15px; -// //height: 60px; -// //padding: 2px 10px; -// //margin-top: 100px; -// //-webkit-transform: translateX(-50%); -// //transform: translateX(-50%); -// transform: translateY(-115px); -// pointer-events: all; -// height: 25px; -// width:fit-content; -// .ProseMirror-example-setup-style hr { -// padding: 2px 10px; -// border: none; -// margin: 1em 0; -// } - -// .ProseMirror-example-setup-style hr:after { -// content: ""; -// display: block; -// height: 1px; -// background-color: silver; -// line-height: 2px; -// } -// } - -// .wrapper { -// position: absolute; -// pointer-events: all; -// } - -// .menuicon { -// display: inline-block; -// border-right: 1px solid white(0, 0, 0, 0.2); -// //color: rgb(19, 18, 18); -// color: rgb(226, 21, 21); -// line-height: 1; -// padding: 0px 2px; -// margin: 1px; -// cursor: pointer; -// text-align: center; -// min-width: 10px; - -// } -// .strong, .heading { font-weight: bold; } -// .em { font-style: italic; } -// .underline {text-decoration: underline} -// .superscript {vertical-align:super} -// .subscript { vertical-align:sub } -// .strikethrough {text-decoration-line:line-through} -// .font-size-indicator { -// font-size: 12px; -// padding-right: 0px; -// } -// .summarize{ -// color: white; -// height: 20px; -// text-align: center; -// } - -// .brush{ -// display: inline-block; -// width: 1em; -// height: 1em; -// stroke-width: 0; -// stroke: currentColor; -// fill: currentColor; -// margin-right: 15px; -// } - -// .brush-active{ -// display: inline-block; -// width: 1em; -// height: 1em; -// stroke-width: 3; -// stroke: greenyellow; -// fill: greenyellow; -// margin-right: 15px; -// } - -// .dragger{ -// color: #eee; -// margin-left: 5px; -// } \ No newline at end of file +} \ No newline at end of file diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 564d9f0df..6001f9840 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -30,9 +30,8 @@ export class TooltipTextMenu { public static Toolbar: HTMLDivElement | undefined; - // editor state + // editor state properties private view: EditorView; - // private editorProps: FieldViewProps & FormattedTextBoxProps | undefined; private fontStyles: MarkType[] = []; private fontSizes: MarkType[] = []; private listTypes: (NodeType | any)[] = []; @@ -42,13 +41,16 @@ export class TooltipTextMenu { private _activeMarks: Mark[] = []; private _marksToDoms: Map = new Map(); private _collapsed: boolean = false; - //private link: HTMLAnchorElement; // editor doms public tooltip: HTMLElement = document.createElement("div"); private wrapper: HTMLDivElement = document.createElement("div"); // editor button doms + private colorDom?: Node; + private colorDropdownDom?: Node; + private highlightDom?: Node; + private highlightDropdownDom?: Node; private linkEditor?: HTMLDivElement; private linkText?: HTMLDivElement; private linkDrag?: HTMLImageElement; @@ -58,42 +60,32 @@ export class TooltipTextMenu { private fontSizeDom?: Node; private fontStyleDom?: Node; private listTypeBtnDom?: Node; - private colorDom?: Node; - private colorDropdownDom?: Node; - private highlightDom?: Node; - private highlightDropdownDom?: Node; - - - // private _collapseBtn?: MenuItem; - // private _brushIsEmpty: boolean = true; - + private basicTools?: HTMLElement; constructor(view: EditorView) { this.view = view; - // // replace old active menu with this - // if (TooltipTextMenuManager.Instance.activeMenu) { - // TooltipTextMenuManager.Instance.activeMenu.wrapper.remove(); - // } - // TooltipTextMenuManager.Instance.activeMenu = this; - - // initialize the tooltip - this.createTooltip(view); + // initialize the tooltip -- sets this.tooltip + this.initTooltip(view); // initialize the wrapper this.wrapper = document.createElement("div"); this.wrapper.className = "wrapper"; this.wrapper.appendChild(this.tooltip); - // positioning? + // initialize the dragger -- appends it to the wrapper + this.createDragger(); + TooltipTextMenu.Toolbar = this.wrapper; } - private async createTooltip(view: EditorView) { + 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 = [ @@ -113,12 +105,15 @@ export class TooltipTextMenu { 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; } @@ -172,19 +167,12 @@ export class TooltipTextMenu { this.tooltip.appendChild(this._brushDropdownDom); // star - // this.tooltip.appendChild(this.createLink().render(this.view).dom); this.tooltip.appendChild(this.createStar().render(this.view).dom); - // + // list types dropdown this.updateListItemDropdown(":", this.listTypeBtnDom); - // await this.updateFromDash(view, undefined, undefined); - // TooltipTextMenu.Toolbar = this.wrapper; - - // dragger - // TODO: onclick handler in drag handles collapsing - this.createDragger(); } initFontStyles() { @@ -224,13 +212,15 @@ export class TooltipTextMenu { this.listTypes = Array.from(this.listTypeToIcon.keys()); } + // creates dragger element that allows dragging and collapsing (on double click) + // of editor and appends it to the wrapper createDragger() { - const dragger = document.createElement("div"); - dragger.className = "dragger"; - let draggerWrapper = document.createElement("div"); draggerWrapper.className = "dragger-wrapper"; + let dragger = document.createElement("div"); + dragger.className = "dragger"; + let line1 = document.createElement("span"); line1.className = "dragger-line"; let line2 = document.createElement("span"); @@ -238,14 +228,72 @@ export class TooltipTextMenu { let line3 = document.createElement("span"); line3.className = "dragger-line"; - draggerWrapper.appendChild(line1); - draggerWrapper.appendChild(line2); - draggerWrapper.appendChild(line3); + dragger.appendChild(line1); + dragger.appendChild(line2); + dragger.appendChild(line3); - dragger.appendChild(draggerWrapper); + draggerWrapper.appendChild(dragger); - this.tooltip.appendChild(dragger); - this.dragElement(dragger); + this.wrapper.appendChild(draggerWrapper); + this.dragElement(draggerWrapper); + } + + dragElement(elmnt: HTMLElement) { + var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; + if (elmnt) { + // if present, the header is where you move the DIV from: + elmnt.onpointerdown = dragMouseDown; + elmnt.ondblclick = onClick; + } + const self = this; + + function dragMouseDown(e: PointerEvent) { + e = e || window.event; + //e.preventDefault(); + // get the mouse cursor position at startup: + pos3 = e.clientX; + pos4 = e.clientY; + document.onpointerup = closeDragElement; + // call a function whenever the cursor moves: + document.onpointermove = elementDrag; + } + + function onClick(e: MouseEvent) { + self._collapsed = !self._collapsed; + const children = self.wrapper.childNodes; + if (self._collapsed && children.length > 0) { + self.wrapper.removeChild(self.tooltip); + self.basicTools && self.wrapper.prepend(self.basicTools); + } + else { + self.wrapper.prepend(self.tooltip); + self.basicTools && self.wrapper.removeChild(self.basicTools); + } + } + + function elementDrag(e: PointerEvent) { + e = e || window.event; + //e.preventDefault(); + // calculate the new cursor position: + pos1 = pos3 - e.clientX; + pos2 = pos4 - e.clientY; + pos3 = e.clientX; + pos4 = e.clientY; + // set the element's new position: + // elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; + // elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; + + self.wrapper.style.top = (self.wrapper.offsetTop - pos2) + "px"; + self.wrapper.style.left = (self.wrapper.offsetLeft - pos1) + "px"; + } + + function closeDragElement() { + // stop moving when mouse button is released: + document.onpointerup = null; + document.onpointermove = null; + //self.highlightSearchTerms(self.state, ["hello"]); + //FormattedTextBox.Instance.unhighlightSearchTerms(); + } } //label of dropdown will change to given label @@ -261,7 +309,7 @@ export class TooltipTextMenu { let newfontSizeDom = (new Dropdown(cut(fontSizeBtns), { label: label, - css: "color:black; min-width: 60px; padding-left: 5px; margin-right: 0;" + css: "color:black; min-width: 60px;" }) as MenuItem).render(this.view).dom; if (this.fontSizeDom) { this.tooltip.replaceChild(newfontSizeDom, this.fontSizeDom); } else { @@ -270,8 +318,6 @@ export class TooltipTextMenu { this.fontSizeDom = newfontSizeDom; } - // Make the DIV element draggable - //label of dropdown will change to given label updateFontStyleDropdown(label: string) { //filtering function - might be unecessary @@ -285,7 +331,7 @@ export class TooltipTextMenu { let newfontStyleDom = (new Dropdown(cut(fontBtns), { label: label, - css: "color:black; width: 125px; margin-left: -3px; padding-left: 2px;" + css: "color:black; width: 125px;" }) as MenuItem).render(this.view).dom; if (this.fontStyleDom) { this.tooltip.replaceChild(newfontStyleDom, this.fontStyleDom); } else { @@ -299,11 +345,7 @@ export class TooltipTextMenu { if (!this.linkEditor || !this.linkText) { this.linkEditor = document.createElement("div"); this.linkEditor.className = "ProseMirror-icon menuicon"; - // this.linkEditor.style.color = "black"; this.linkText = document.createElement("div"); - // this.linkText.style.cssFloat = "left"; - // this.linkText.style.marginRight = "5px"; - // this.linkText.style.marginLeft = "5px"; this.linkText.setAttribute("contenteditable", "true"); this.linkText.style.whiteSpace = "nowrap"; this.linkText.style.width = "150px"; @@ -391,13 +433,11 @@ export class TooltipTextMenu { e.preventDefault(); } }; - // this.tooltip.appendChild(this.linkEditor); } async getTextLinkTargetTitle() { let node = this.view.state.selection.$from.nodeAfter; let link = node && node.marks.find(m => m.type.name === "link"); - // let href = link!.attrs.href; if (link) { let href = link.attrs.href; if (href) { @@ -445,7 +485,6 @@ export class TooltipTextMenu { input.type = "text"; input.placeholder = "Enter URL"; - console.log(targetTitle); if (targetTitle) input.value = targetTitle; input.onclick = (e: MouseEvent) => { input.select(); @@ -514,62 +553,6 @@ export class TooltipTextMenu { return linkDropdown; } - dragElement(elmnt: HTMLElement) { - var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; - if (elmnt) { - // if present, the header is where you move the DIV from: - elmnt.onpointerdown = dragMouseDown; - elmnt.ondblclick = onClick; - } - const self = this; - - function dragMouseDown(e: PointerEvent) { - e = e || window.event; - //e.preventDefault(); - // get the mouse cursor position at startup: - pos3 = e.clientX; - pos4 = e.clientY; - document.onpointerup = closeDragElement; - // call a function whenever the cursor moves: - document.onpointermove = elementDrag; - } - - function onClick(e: MouseEvent) { - self._collapsed = !self._collapsed; - const children = self.wrapper.childNodes; - if (self._collapsed && children.length > 1) { - self.wrapper.removeChild(self.tooltip); - } - else { - self.wrapper.appendChild(self.tooltip); - } - } - - function elementDrag(e: PointerEvent) { - e = e || window.event; - //e.preventDefault(); - // calculate the new cursor position: - pos1 = pos3 - e.clientX; - pos2 = pos4 - e.clientY; - pos3 = e.clientX; - pos4 = e.clientY; - // set the element's new position: - // elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; - // elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; - - self.wrapper.style.top = (self.wrapper.offsetTop - pos2) + "px"; - self.wrapper.style.left = (self.wrapper.offsetLeft - pos1) + "px"; - } - - function closeDragElement() { - // stop moving when mouse button is released: - document.onpointerup = null; - document.onpointermove = null; - //self.highlightSearchTerms(self.state, ["hello"]); - //FormattedTextBox.Instance.unhighlightSearchTerms(); - } - } - // makeLinkWithState = (state: EditorState, target: string, location: string) => { // let link = state.schema.mark(state.schema.marks.link, { href: target, location: location }); // } @@ -616,10 +599,70 @@ export class TooltipTextMenu { } } } + } - + 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 + }); + } + }); + } //will display a remove-list-type button if selection is in list, otherwise will show list type dropdown updateListItemDropdown(label: string, listTypeBtn: any) { @@ -644,108 +687,6 @@ export class TooltipTextMenu { return listTypeBtn; } - //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected text - 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) { - // fontsize - if (markType.name[0] === 'p') { - let size = this.fontSizeToNum.get(markType); - 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; - } - } - } - else { - let fontName = this.fontStylesToName.get(markType); - 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: 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); - } - } - - //remove all node typeand apply the passed-in one to the selected text - changeToNodeType = (nodeType: NodeType | undefined, view: EditorView) => { - //remove oldif (nodeType) { //add new - 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 as any).attrs.mapStyle); - marks && tx3.ensureMarks([...marks]); - marks && tx3.setStoredMarks([...marks]); - - view.dispatch(tx2); - })) { - let tx2 = view.state.tr; - let tx3 = nodeType ? updateBullets(tx2, schema, (nodeType as any).attrs.mapStyle) : tx2; - 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 - dropdownMarkBtn(label: string, css: string, markType: MarkType, view: EditorView, changeToMarkInGroup: (markType: MarkType, view: EditorView, groupMarks: MarkType[]) => any, groupMarks: MarkType[]) { - return new MenuItem({ - title: "", - label: label, - execEvent: "", - class: "menuicon", - css: css, - enable() { return true; }, - run() { - changeToMarkInGroup(markType, view, groupMarks); - } - }); - } - createStar() { return new MenuItem({ title: "Summarize", @@ -772,6 +713,17 @@ export class TooltipTextMenu { return true; } + public static insertComment(state: EditorState, 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; + } + createHighlightTool() { return new MenuItem({ title: "Highlight", @@ -975,24 +927,6 @@ export class TooltipTextMenu { return colorDropdown; } - 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(); - } - }); - } - createBrush(active: boolean = false) { const icon = { height: 32, width: 32, @@ -1020,8 +954,6 @@ export class TooltipTextMenu { }); } - // selectionchanged event handler - brush_function(state: EditorState, dispatch: any) { if (TooltipTextMenuManager.Instance._brushIsEmpty) { const selected_marks = this.getMarksInSelection(this.view.state); @@ -1055,8 +987,6 @@ export class TooltipTextMenu { } } } - - } createBrushDropdown(active: boolean = false) { @@ -1120,88 +1050,106 @@ export class TooltipTextMenu { return brushDom; } - // createCollapse() { - // this._collapseBtn = new MenuItem({ - // title: "Collapse", - // //label: "Collapse", - // icon: icons.join, - // execEvent: "", - // css: "color:white;", - // class: "summarize", - // run: () => { - // this.collapseToolTip(); - // } - // }); - // } - // collapseToolTip() { - // if (this._collapseBtn) { - // if (this._collapseBtn.spec.title === "Collapse") { - // // const newcollapseBtn = new MenuItem({ - // // title: "Expand", - // // icon: icons.join, - // // execEvent: "", - // // css: "color:white;", - // // class: "summarize", - // // run: (state, dispatch, view) => { - // // this.collapseToolTip(); - // // } - // // }); - // // this.tooltip.replaceChild(newcollapseBtn.render(this.view).dom, this._collapseBtn.render(this.view).dom); - // // this._collapseBtn = newcollapseBtn; - // this.tooltip.style.width = "30px"; - // this._collapseBtn.spec.title = "Expand"; - // this._collapseBtn.render(this.view); - // } - // else { - // this._collapseBtn.spec.title = "Collapse"; - // this.tooltip.style.width = "550px"; - // this._collapseBtn.render(this.view); - // } - // } - // } - 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)) { + //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected text + changeToMarkInGroup = (markType: MarkType | undefined, view: EditorView, fontMarks: MarkType[]) => { + let { $cursor, ranges } = view.state.selection as TextSelection; + let state = view.state; + let dispatch = view.dispatch; - 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; + //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); + } + } } - // 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 - }); + } + }); + + if (markType) { + // fontsize + if (markType.name[0] === 'p') { + let size = this.fontSizeToNum.get(markType); + 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; + } + } + } + else { + let fontName = this.fontStylesToName.get(markType); + 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: 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); + } + } + + //remove all node typeand apply the passed-in one to the selected text + changeToNodeType = (nodeType: NodeType | undefined, view: EditorView) => { + //remove oldif (nodeType) { //add new + 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 as any).attrs.mapStyle); + marks && tx3.ensureMarks([...marks]); + marks && tx3.setStoredMarks([...marks]); + + view.dispatch(tx2); + })) { + let tx2 = view.state.tr; + let tx3 = nodeType ? updateBullets(tx2, schema, (nodeType as any).attrs.mapStyle) : tx2; + 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 + dropdownMarkBtn(label: string, css: string, markType: MarkType, view: EditorView, changeToMarkInGroup: (markType: MarkType, view: EditorView, groupMarks: MarkType[]) => any, groupMarks: MarkType[]) { + return new MenuItem({ + title: "", + label: label, + execEvent: "", + class: "dropdown-item", + css: css, + enable() { return true; }, + run() { + changeToMarkInGroup(markType, view, groupMarks); } }); } @@ -1213,7 +1161,7 @@ export class TooltipTextMenu { title: "", label: label, execEvent: "", - class: "menuicon", + class: "dropdown-item", css: css, enable() { return true; }, run() { @@ -1390,35 +1338,12 @@ export class TooltipTextMenu { update_mark_doms() { this.reset_mark_doms(); - let foundlink = false; - // let children = this.extras.childNodes; this._activeMarks.forEach((mark) => { if (this._marksToDoms.has(mark)) { let dom = this._marksToDoms.get(mark); if (dom) dom.style.color = "greenyellow"; } - // if (children.length > 1) { - // foundlink = true; - // } - // if (mark.type.name === "link" && children.length === 1) { - // // let del = document.createElement("button"); - // // del.textContent = "X"; - // // del.style.color = "red"; - // // del.style.height = "10px"; - // // del.style.width = "10px"; - // // del.style.marginLeft = "5px"; - // // del.onclick = this.deleteLink; - // // this.extras.appendChild(del); - // let del = this.deleteLinkItem().render(this.view).dom; - // this.extras.appendChild(del); - // foundlink = true; - // } }); - // if (!foundlink) { - // if (children.length > 1) { - // this.extras.removeChild(children[1]); - // } - // } // keeps brush tool highlighted if active when switching between textboxes if (!TooltipTextMenuManager.Instance._brushIsEmpty) { @@ -1535,30 +1460,11 @@ class TooltipTextMenuManager { return TooltipTextMenuManager._instance; } - // private pinnedToUnpinned() { - // let position = MainOverlayTextBox.Instance.position; - - // this.unpinnedX = this.pinnedX - position[0]; - // this.unpinnedY = this.pinnedY - position[1]; - // } - - // private unpinnedToPinned() { - // let position = MainOverlayTextBox.Instance.position; - - // this.pinnedX = position[0] + this.unpinnedX; - // this.pinnedY = position[1] + this.unpinnedY; - // } - public get isPinned() { return this._isPinned; } public toggleIsPinned() { - // if (this._isPinned) { - // this.pinnedToUnpinned(); - // } else { - // this.unpinnedToPinned(); - // } this._isPinned = !this._isPinned; } } -- cgit v1.2.3-70-g09d2 From 1f1707c8756c858e45e1f89cd0ff035231e15225 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 15 Nov 2019 13:52:23 -0500 Subject: fixed errors --- .vscode/settings.json | 2 +- src/client/util/TooltipTextMenu.tsx | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/.vscode/settings.json b/.vscode/settings.json index c999af8b8..0b45c3f46 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,5 +11,5 @@ "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false, "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true, "search.usePCRE2": true, - "typescript.tsdk": "node_modules\\typescript\\lib" + "typescript.tsdk": "node_modules/typescript/lib" } \ No newline at end of file diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 78917c779..0dc63668b 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -33,12 +33,10 @@ export class TooltipTextMenu { // editor state properties private view: EditorView; private editorProps: FieldViewProps & FormattedTextBoxProps | undefined; - - private fontStyles: MarkType[] = []; - private fontSizes: MarkType[] = []; + + private fontStyles: Mark[] = []; + private fontSizes: Mark[] = []; private listTypes: (NodeType | any)[] = []; - private fontSizeToNum: Map = new Map(); - private fontStylesToName: Map = new Map(); private listTypeToIcon: Map = new Map(); private _activeMarks: Mark[] = []; private _marksToDoms: Map = new Map(); -- cgit v1.2.3-70-g09d2 From 79c66275c60001e6d7aa997d9802ec0fed893205 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 15 Nov 2019 15:03:08 -0500 Subject: fixed toolbar disappearing with multiple text boxes. --- src/client/util/TooltipTextMenu.tsx | 6 ++---- src/client/views/DocumentDecorations.tsx | 4 ++-- src/client/views/nodes/FormattedTextBox.tsx | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 0dc63668b..14f8b2bd4 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -1,6 +1,4 @@ -import { library, dom } from '@fortawesome/fontawesome-svg-core'; -import { faListUl } from '@fortawesome/free-solid-svg-icons'; -import { action, observable } from "mobx"; +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'; @@ -1482,7 +1480,7 @@ export class TooltipTextMenu { } destroy() { - this.wrapper.remove(); + // this.wrapper.remove(); } } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 49921170f..890c32bcb 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -548,8 +548,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } } public showTextBar = () => { - if (this.TextBar) { - TooltipTextMenu.Toolbar && Array.from(this.TextBar.childNodes).indexOf(TooltipTextMenu.Toolbar) === -1 && this.TextBar.appendChild(TooltipTextMenu.Toolbar); + if (this.TextBar && TooltipTextMenu.Toolbar && Array.from(this.TextBar.childNodes).indexOf(TooltipTextMenu.Toolbar) === -1) { + this.TextBar.appendChild(TooltipTextMenu.Toolbar); } } render() { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 0a535045b..35212b732 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -17,7 +17,7 @@ import { Copy, Id } from '../../../new_fields/FieldSymbols'; import { RichTextField } from "../../../new_fields/RichTextField"; import { RichTextUtils } from '../../../new_fields/RichTextUtils'; import { createSchema, makeInterface } from "../../../new_fields/Schema"; -import { Cast, DateCast, NumCast, StrCast } from "../../../new_fields/Types"; +import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { numberRange, Utils, addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, returnOne } from '../../../Utils'; import { GoogleApiClientUtils, Pulls, Pushes } from '../../apis/google_docs/GoogleApiClientUtils'; import { DocServer } from "../../DocServer"; -- cgit v1.2.3-70-g09d2 From 2ab22e64e9b95e6b76566c493ae90f5951475e72 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 15 Nov 2019 15:11:26 -0500 Subject: fixed naming of 'marker' mark --- src/client/util/RichTextSchema.tsx | 2 +- src/client/util/TooltipTextMenu.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 0e8c9f14c..0a717dff1 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -326,7 +326,7 @@ export const marks: { [index: string]: MarkSpec } = { } }, - highlight2: { + marker: { attrs: { highlight: { default: "transparent" } }, diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 14f8b2bd4..5136089b3 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -735,7 +735,7 @@ export class TooltipTextMenu { public static insertHighlight(color: String, state: EditorState, dispatch: any) { if (state.selection.empty) return false; - let highlightMark = state.schema.mark(state.schema.marks.highlight2, { highlight: color }); + let highlightMark = state.schema.mark(state.schema.marks.marker, { highlight: color }); dispatch(state.tr.addMark(state.selection.from, state.selection.to, highlightMark)); } @@ -1223,7 +1223,7 @@ export class TooltipTextMenu { }); } - markActive = function (state: EditorState, type: MarkType>) { + markActive = function(state: EditorState, type: MarkType>) { let { from, $from, to, empty } = state.selection; if (empty) return type.isInSet(state.storedMarks || $from.marks()); else return state.doc.rangeHasMark(from, to, type); -- cgit v1.2.3-70-g09d2