aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFawn <fangrui_tong@brown.edu>2020-01-07 08:33:27 -0500
committerFawn <fangrui_tong@brown.edu>2020-01-07 08:33:27 -0500
commit73403134ceae1e5665e8021175a54b0953541d23 (patch)
tree90a881e84dc7065d952416bdbd1e11f166eb3d18
parent372d8623ce5b0fd545739cb53a38f6d3f759e7e4 (diff)
started integrating link button with richtextmenu
-rw-r--r--src/client/util/RichTextMenu.tsx176
1 files changed, 108 insertions, 68 deletions
diff --git a/src/client/util/RichTextMenu.tsx b/src/client/util/RichTextMenu.tsx
index a9de87572..12bd3348f 100644
--- a/src/client/util/RichTextMenu.tsx
+++ b/src/client/util/RichTextMenu.tsx
@@ -8,7 +8,7 @@ import { EditorView } from "prosemirror-view";
import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faBold, faItalic, faUnderline, faStrikethrough, faSubscript, faSuperscript, faIndent, faEyeDropper, faCaretDown, faPalette, faHighlighter } from "@fortawesome/free-solid-svg-icons";
+import { faBold, faItalic, faUnderline, faStrikethrough, faSubscript, faSuperscript, faIndent, faEyeDropper, faCaretDown, faPalette, faHighlighter, faLink } from "@fortawesome/free-solid-svg-icons";
import { MenuItem, Dropdown } from "prosemirror-menu";
import { updateBullets } from "./ProsemirrorExampleTransfer";
import { FieldViewProps } from "../views/nodes/FieldView";
@@ -20,7 +20,7 @@ import { PastelSchemaPalette, DarkPastelSchemaPalette } from '../../new_fields/S
import "./RichTextMenu.scss";
const { toggleMark, setBlockType } = require("prosemirror-commands");
-library.add(faBold, faItalic, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, faCaretDown, faPalette, faHighlighter);
+library.add(faBold, faItalic, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, faCaretDown, faPalette, faHighlighter, faLink);
@observer
export default class RichTextMenu extends AntimodeMenu {
@@ -43,6 +43,9 @@ export default class RichTextMenu extends AntimodeMenu {
@observable private activeHighlightColor: string = "transparent";
@observable private showHighlightDropdown: boolean = false;
+ @observable private currentLink: string | undefined = "";
+ @observable private showLinkDropdown: boolean = false;
+
constructor(props: Readonly<{}>) {
super(props);
RichTextMenu.Instance = this;
@@ -67,9 +70,8 @@ export default class RichTextMenu extends AntimodeMenu {
const state = view.state;
// DocumentDecorations.Instance.showTextBar();
props && (this.editorProps = props);
- // // Don't do anything if the document/selection didn't change
- // if (lastState && lastState.doc.eq(state.doc) &&
- // lastState.selection.eq(state.selection)) return;
+ // Don't do anything if the document/selection didn't change
+ if (lastState && lastState.doc.eq(state.doc) && lastState.selection.eq(state.selection)) return;
// this.reset_mark_doms();
@@ -147,15 +149,9 @@ export default class RichTextMenu extends AntimodeMenu {
createButton(faIcon: string, title: string, command?: any, onclick?: any) {
const self = this;
function onClick(e: React.PointerEvent) {
- console.log("clicked button");
- // dom.addEventListener("pointerdown", e => {
e.preventDefault();
- self.view && self.view.focus();
- // if (dom.contains(e.target as Node)) {
e.stopPropagation();
- // command(this.view.state, this.view.dispatch, this.view);
- // }
- // });
+ self.view && self.view.focus();
self.view && command && command(self.view!.state, self.view!.dispatch, self.view);
self.view && onclick && onclick(self.view!.state, self.view!.dispatch, self.view);
}
@@ -310,9 +306,7 @@ export default class RichTextMenu extends AntimodeMenu {
return (
<div className="button-dropdown-wrapper">
- <button className="antimodeMenu-button" title="" onPointerDown={onBrushClick}>
- <FontAwesomeIcon icon="eye-dropper" size="lg" />
- </button>
+ <button className="antimodeMenu-button" title="" onPointerDown={onBrushClick}><FontAwesomeIcon icon="eye-dropper" size="lg" /></button>
<button className="dropdown-button antimodeMenu-button" onPointerDown={onDropdownClick}><FontAwesomeIcon icon="caret-down" size="sm" /></button>
{this.showBrushDropdown ?
(<div className="dropdown">
@@ -400,9 +394,7 @@ export default class RichTextMenu extends AntimodeMenu {
return (
<div className="button-dropdown-wrapper">
- <button className="antimodeMenu-button" title="" onPointerDown={onColorClick}>
- <FontAwesomeIcon icon="palette" size="lg" />
- </button>
+ <button className="antimodeMenu-button" title="" onPointerDown={onColorClick}><FontAwesomeIcon icon="palette" size="lg" /></button>
<button className="dropdown-button antimodeMenu-button" onPointerDown={onDropdownClick}><FontAwesomeIcon icon="caret-down" size="sm" /></button>
{this.showColorDropdown ?
(<div className="dropdown">
@@ -464,9 +456,7 @@ export default class RichTextMenu extends AntimodeMenu {
return (
<div className="button-dropdown-wrapper">
- <button className="antimodeMenu-button" title="" onPointerDown={onHighlightClick}>
- <FontAwesomeIcon icon="highlighter" size="lg" />
- </button>
+ <button className="antimodeMenu-button" title="" onPointerDown={onHighlightClick}><FontAwesomeIcon icon="highlighter" size="lg" /></button>
<button className="dropdown-button antimodeMenu-button" onPointerDown={onDropdownClick}><FontAwesomeIcon icon="caret-down" size="sm" /></button>
{this.showHighlightDropdown ?
(<div className="dropdown">
@@ -484,6 +474,102 @@ export default class RichTextMenu extends AntimodeMenu {
toggleMark(state.schema.marks.marker, { highlight: color })(state, dispatch);
}
+ @action toggleLinkDropdown() { this.showLinkDropdown = !this.showLinkDropdown; }
+ @action setCurrentLink(link: string) { this.currentLink = link; }
+
+ createLinkButton() {
+ const self = this;
+ function onDropdownClick(e: React.PointerEvent) {
+ e.preventDefault();
+ e.stopPropagation();
+ self.view && self.view.focus();
+ self.toggleLinkDropdown();
+ }
+ function onLinkChange(e: React.ChangeEvent<HTMLInputElement>) {
+ self.setCurrentLink(e.target.value);
+ }
+
+ const targetTitle = await this.getTextLinkTargetTitle();
+ console.log(targetTitle);
+ // this.setCurrentLink(targetTitle);
+
+ return (
+ <div className="button-dropdown-wrapper">
+ <button className="antimodeMenu-button" title="" onPointerDown={onDropdownClick}><FontAwesomeIcon icon="link" size="lg" /></button>
+ <button className="dropdown-button antimodeMenu-button" onPointerDown={onDropdownClick}><FontAwesomeIcon icon="caret-down" size="sm" /></button>
+ {this.showLinkDropdown ?
+ (<div className="dropdown">
+ <p>Linked to:</p>
+ {/* <input value={this.currentLink} placeholder="Enter URL" onChange={onLinkChange}/>
+ <button onPointerDown={e => this.makeLinkToURL("input value", "onRight")}>Apply hyperlink</button>
+ <button onPointerDown={e => this.deleteLink()}>Remove link</button> */}
+ </div>)
+ : <></> }
+ </div>
+ );
+ }
+
+ async getTextLinkTargetTitle() {
+ const node = this.view.state.selection.$from.nodeAfter;
+ const link = node && node.marks.find(m => m.type.name === "link");
+ if (link) {
+ const href = link.attrs.href;
+ if (href) {
+ if (href.indexOf(Utils.prepend("/doc/")) === 0) {
+ const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
+ if (linkclicked) {
+ const linkDoc = await DocServer.GetRefField(linkclicked);
+ if (linkDoc instanceof Doc) {
+ const anchor1 = await Cast(linkDoc.anchor1, Doc);
+ const anchor2 = await Cast(linkDoc.anchor2, Doc);
+ const currentDoc = SelectionManager.SelectedDocuments().length && SelectionManager.SelectedDocuments()[0].props.Document;
+ if (currentDoc && anchor1 && anchor2) {
+ if (Doc.AreProtosEqual(currentDoc, anchor1)) {
+ return StrCast(anchor2.title);
+ }
+ if (Doc.AreProtosEqual(currentDoc, anchor2)) {
+ return StrCast(anchor1.title);
+ }
+ }
+ }
+ }
+ } else {
+ return href;
+ }
+ } else {
+ return link.attrs.title;
+ }
+ }
+ }
+
+ makeLinkToURL = (target: String, lcoation: string) => {
+ let node = this.view.state.selection.$from.nodeAfter;
+ let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target, location: location });
+ this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link));
+ this.view.dispatch(this.view.state.tr.addMark(this.view.state.selection.from, this.view.state.selection.to, link));
+ node = this.view.state.selection.$from.nodeAfter;
+ link = node && node.marks.find(m => m.type.name === "link");
+ }
+
+ deleteLink = () => {
+ const node = this.view.state.selection.$from.nodeAfter;
+ const link = node && node.marks.find(m => m.type === this.view.state.schema.marks.link);
+ const href = link!.attrs.href;
+ if (href) {
+ if (href.indexOf(Utils.prepend("/doc/")) === 0) {
+ const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
+ if (linkclicked) {
+ DocServer.GetRefField(linkclicked).then(async linkDoc => {
+ if (linkDoc instanceof Doc) {
+ LinkManager.Instance.deleteLink(linkDoc);
+ this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link));
+ }
+ });
+ }
+ }
+ }
+ }
+
reference_node(pos: ResolvedPos<any>): ProsNode | null {
if (!this.view) return null;
@@ -543,26 +629,6 @@ export default class RichTextMenu extends AntimodeMenu {
{ mark: null, title: "", label: "default", command: unimplementedFunction, hidden: true },
];
- // //will display a remove-list-type button if selection is in list, otherwise will show list type dropdown
- // updateListItemDropdown(label: string, listTypeBtn: any) {
- // //remove old btn
- // if (listTypeBtn) { this.tooltip.removeChild(listTypeBtn); }
-
- // //Make a dropdown of all list types
- // const toAdd: MenuItem[] = [];
- // this.listTypeToIcon.forEach((icon, type) => {
- // toAdd.push(this.dropdownBulletBtn(icon, "color: black; width: 40px;", type, this.view, this.listTypes, this.changeBulletType));
- // });
- // //option to remove the list formatting
- // toAdd.push(this.dropdownBulletBtn("X", "color: black; width: 40px;", undefined, this.view, this.listTypes, this.changeBulletType));
-
- // listTypeBtn = (new Dropdown(toAdd, { label: label, css: "color:black; width: 40px;" }) as MenuItem).render(this.view).dom;
-
- // //add this new button and return it
- // this.tooltip.appendChild(listTypeBtn);
- // return listTypeBtn;
- // }
-
const listTypeOptions = [
{ node: schema.nodes.ordered_list.create({ mapStyle: "bullet" }), title: "Set list type", label: ":", command: this.changeListType },
{ node: schema.nodes.ordered_list.create({ mapStyle: "decimal" }), title: "Set list type", label: "1.1", command: this.changeListType },
@@ -570,8 +636,6 @@ export default class RichTextMenu extends AntimodeMenu {
{ node: undefined, title: "Set list type", label: "Remove", command: this.changeListType },
];
- // options: { node: NodeType | null, title: string, label: string, command: (node: NodeType) => void, hidden ?: boolean } []
-
const buttons = [
this.createButton("bold", "Bold", toggleMark(schema.marks.strong)),
this.createButton("italic", "Italic", toggleMark(schema.marks.em)),
@@ -581,6 +645,7 @@ export default class RichTextMenu extends AntimodeMenu {
this.createButton("subscript", "Subscript", toggleMark(schema.marks.subscript)),
this.createColorButton(),
this.createHighlighterButton(),
+ this.createLinkButton(),
this.createBrushButton(),
this.createButton("indent", "Summarize", undefined, this.insertSummarizer),
this.createMarksDropdown(this.activeFontSize, fontSizeOptions),
@@ -588,31 +653,6 @@ export default class RichTextMenu extends AntimodeMenu {
this.createNodesDropdown(this.activeListType, listTypeOptions),
];
- // this._marksToDoms = new Map();
- // items.forEach(({ title, dom, command }) => {
- // // this.tooltip.appendChild(dom);
- // switch (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;
- // }
-
- // //pointer down handler to activate button effects
- // dom.addEventListener("pointerdown", e => {
- // e.preventDefault();
- // this.view.focus();
- // if (dom.contains(e.target as Node)) {
- // e.stopPropagation();
- // command(this.view.state, this.view.dispatch, this.view);
- // }
- // });
- // });
-
return this.getElement(buttons);
}
} \ No newline at end of file