aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFawn <fangrui_tong@brown.edu>2019-10-10 11:44:11 -0400
committerFawn <fangrui_tong@brown.edu>2019-10-10 11:44:11 -0400
commit3a5e787e3a3c42ec3208f9f75bd8bfbac71aeb81 (patch)
tree9bb597df3bb4d9700cd57ee52b026c26c125a245 /src
parentdbf41b6eaaebe16567289ba4814316b2c79e0b65 (diff)
added dropdowns to link tool and brush tool
Diffstat (limited to 'src')
-rw-r--r--src/client/util/TooltipTextMenu.tsx315
1 files changed, 221 insertions, 94 deletions
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<Field>) => {
- // if (f instanceof Doc) {
- // if (DocumentManager.Instance.getDocumentView(f)) {
- // DocumentManager.Instance.getDocumentView(f)!.props.focus(f, false);
- // }
- // else this.editorProps && this.editorProps.addDocTab(f, undefined, "onRight");
- // }
- // }));
- // }
- // // TODO This should have an else to handle external links
- // e.stopPropagation();
- // e.preventDefault();
- // }
- // };
- // this.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) {