aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvellichora <fangrui_tong@brown.edu>2020-01-07 05:23:53 -0500
committervellichora <fangrui_tong@brown.edu>2020-01-07 05:23:53 -0500
commit7d58927ba9da5ea146ef6daed336036b01447302 (patch)
tree0fc89c460a403e8681ff85171558af164e1a7f8b
parent80d4bf70cb8e29f15d8e51b1e0bd378a26d68fcf (diff)
added summarizing button to richtextmenu
-rw-r--r--src/client/util/RichTextMenu.tsx215
1 files changed, 134 insertions, 81 deletions
diff --git a/src/client/util/RichTextMenu.tsx b/src/client/util/RichTextMenu.tsx
index 7e20d126c..6b6a2620e 100644
--- a/src/client/util/RichTextMenu.tsx
+++ b/src/client/util/RichTextMenu.tsx
@@ -8,16 +8,17 @@ import { EditorView } from "prosemirror-view";
import { EditorState, NodeSelection } from "prosemirror-state";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faBold, faItalic, faUnderline, faStrikethrough, faSubscript, faSuperscript } from "@fortawesome/free-solid-svg-icons";
+import { faBold, faItalic, faUnderline, faStrikethrough, faSubscript, faSuperscript, faIndent } from "@fortawesome/free-solid-svg-icons";
import { MenuItem, Dropdown } from "prosemirror-menu";
import { updateBullets } from "./ProsemirrorExampleTransfer";
import { FieldViewProps } from "../views/nodes/FieldView";
import { NumCast } from "../../new_fields/Types";
import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox";
import { unimplementedFunction } from "../../Utils";
+import { wrapInList } from "prosemirror-schema-list";
const { toggleMark, setBlockType } = require("prosemirror-commands");
-library.add(faBold, faItalic, faUnderline, faStrikethrough, faSuperscript, faSubscript);
+library.add(faBold, faItalic, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent);
@observer
export default class RichTextMenu extends AntimodeMenu {
@@ -26,10 +27,9 @@ export default class RichTextMenu extends AntimodeMenu {
private view?: EditorView;
private editorProps: FieldViewProps & FormattedTextBoxProps | undefined;
- private _marksToDoms: Map<Mark, JSX.Element> = new Map();
-
@observable private activeFontSize: string = "";
@observable private activeFontFamily: string = "";
+ @observable private activeListType: string = "";
constructor(props: Readonly<{}>) {
super(props);
@@ -41,12 +41,7 @@ export default class RichTextMenu extends AntimodeMenu {
this.view = view;
}
- // update() {
- // console.log("update");
- // }
-
update(view: EditorView, lastState: EditorState | undefined) {
- console.log("update");
this.updateFromDash(view, lastState, this.editorProps);
}
@@ -58,7 +53,6 @@ export default class RichTextMenu extends AntimodeMenu {
}
this.view = view;
const state = view.state;
- console.log("update from dash");
// DocumentDecorations.Instance.showTextBar();
props && (this.editorProps = props);
// // Don't do anything if the document/selection didn't change
@@ -84,14 +78,57 @@ export default class RichTextMenu extends AntimodeMenu {
// this.update_mark_doms();
}
+ setMark = (mark: Mark, state: EditorState<any>, dispatch: any) => {
+ if (mark) {
+ const node = (state.selection as NodeSelection).node;
+ if (node?.type === schema.nodes.ordered_list) {
+ let attrs = node.attrs;
+ if (mark.type === schema.marks.pFontFamily) attrs = { ...attrs, setFontFamily: mark.attrs.family };
+ if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, setFontSize: mark.attrs.fontSize };
+ if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, setFontColor: mark.attrs.color };
+ const tr = updateBullets(state.tr.setNodeMarkup(state.selection.from, node.type, attrs), state.schema);
+ dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(state.selection.from))));
+ } else {
+ toggleMark(mark.type, mark.attrs)(state, (tx: any) => {
+ const { from, $from, to, empty } = tx.selection;
+ if (!tx.doc.rangeHasMark(from, to, mark.type)) {
+ toggleMark(mark.type, mark.attrs)({ tr: tx, doc: tx.doc, selection: tx.selection, storedMarks: tx.storedMarks }, dispatch);
+ } else dispatch(tx);
+ });
+ }
+ }
+ }
+
+ // finds font sizes and families in selection
+ getActiveFontStylesOnSelection() {
+ if (!this.view) return;
+
+ const activeFamilies: string[] = [];
+ const activeSizes: string[] = [];
+ const state = this.view.state;
+ const pos = this.view.state.selection.$from;
+ const ref_node = this.reference_node(pos);
+ if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) {
+ ref_node.marks.forEach(m => {
+ m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family);
+ m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "pt");
+ });
+ }
+
+ let styles = new Map<String, String[]>();
+ styles.set("families", activeFamilies);
+ styles.set("sizes", activeSizes);
+ return styles;
+ }
destroy() {
console.log("destroy");
}
- createButton(faIcon: string, title: string, command: any) {
+ 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();
@@ -100,7 +137,8 @@ export default class RichTextMenu extends AntimodeMenu {
// command(this.view.state, this.view.dispatch, this.view);
// }
// });
- self.view && command(self.view!.state, self.view!.dispatch, self.view);
+ self.view && command && command(self.view!.state, self.view!.dispatch, self.view);
+ self.view && onclick && onclick(self.view!.state, self.view!.dispatch, self.view);
}
return (
@@ -131,48 +169,29 @@ export default class RichTextMenu extends AntimodeMenu {
});
}
return <select onChange={e => onChange(e.target.value)}>{items}</select>;
-
- // let items: MenuItem[] = [];
- // options.forEach(({ mark, title, label, command }) => {
- // const self = this;
- // function onSelect() {
- // self.view && command(mark, self.view);
- // }
- // // this.createMarksOption("Set font size", String(mark.attrs.fontSize), onSelect)
- // items.push(
- // new MenuItem({
- // title: title,
- // label: label,
- // execEvent: "",
- // class: "dropdown-item",
- // css: "",
- // run() { onSelect(); }
- // })
- // );
- // });
-
- // return <div>{(new Dropdown(items, { label: label }) as MenuItem).render(this.view!).dom}</div>;
}
- setMark = (mark: Mark, state: EditorState<any>, dispatch: any) => {
- if (mark) {
- const node = (state.selection as NodeSelection).node;
- if (node?.type === schema.nodes.ordered_list) {
- let attrs = node.attrs;
- if (mark.type === schema.marks.pFontFamily) attrs = { ...attrs, setFontFamily: mark.attrs.family };
- if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, setFontSize: mark.attrs.fontSize };
- if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, setFontColor: mark.attrs.color };
- const tr = updateBullets(state.tr.setNodeMarkup(state.selection.from, node.type, attrs), state.schema);
- dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(state.selection.from))));
- } else {
- toggleMark(mark.type, mark.attrs)(state, (tx: any) => {
- const { from, $from, to, empty } = tx.selection;
- if (!tx.doc.rangeHasMark(from, to, mark.type)) {
- toggleMark(mark.type, mark.attrs)({ tr: tx, doc: tx.doc, selection: tx.selection, storedMarks: tx.storedMarks }, dispatch);
- } else dispatch(tx);
- });
+ createNodesDropdown(activeOption: string, options: { node: NodeType | any | null, title: string, label: string, command: (node: NodeType | any) => void, hidden?: boolean }[]): JSX.Element {
+ let items = options.map(({ title, label, hidden }) => {
+ if (hidden) {
+ return label === activeOption ?
+ <option value={label} title={title} selected hidden>{label}</option> :
+ <option value={label} title={title} hidden>{label}</option>;
}
+ return label === activeOption ?
+ <option value={label} title={title} selected>{label}</option> :
+ <option value={label} title={title}>{label}</option>;
+ });
+
+ const self = this;
+ function onChange(val: string) {
+ options.forEach(({ label, node, command }) => {
+ if (val === label) {
+ self.view && node && command(node);
+ }
+ });
}
+ return <select onChange={e => onChange(e.target.value)}>{items}</select>;
}
changeFontSize = (mark: Mark, view: EditorView) => {
@@ -201,40 +220,44 @@ export default class RichTextMenu extends AntimodeMenu {
this.setMark(view.state.schema.marks.pFontFamily.create({ family: fontName }), view.state, view.dispatch);
}
- // finds font sizes and families in selection
- getActiveFontStylesOnSelection() {
+ // TODO: remove doesn't work
+ //remove all node type and apply the passed-in one to the selected text
+ changeListType = (nodeType: NodeType | undefined) => {
+ console.log("change the list type ");
if (!this.view) return;
-
- const activeFamilies: string[] = [];
- const activeSizes: string[] = [];
- const state = this.view.state;
- const pos = this.view.state.selection.$from;
- const ref_node = this.reference_node(pos);
- if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) {
- ref_node.marks.forEach(m => {
- m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family);
- m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "pt");
- });
+ console.log("change the list type has view");
+
+ if (nodeType === schema.nodes.bullet_list) {
+ wrapInList(nodeType)(this.view.state, this.view.dispatch);
+ } else {
+ const marks = this.view.state.storedMarks || (this.view.state.selection.$to.parentOffset && this.view.state.selection.$from.marks());
+ if (!wrapInList(schema.nodes.ordered_list)(this.view.state, (tx2: any) => {
+ const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle);
+ marks && tx3.ensureMarks([...marks]);
+ marks && tx3.setStoredMarks([...marks]);
+
+ this.view!.dispatch(tx2);
+ })) {
+ const tx2 = this.view.state.tr;
+ const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle);
+ marks && tx3.ensureMarks([...marks]);
+ marks && tx3.setStoredMarks([...marks]);
+
+ this.view.dispatch(tx3);
+ }
}
-
- let styles = new Map<String, String[]>();
- styles.set("families", activeFamilies);
- styles.set("sizes", activeSizes);
- return styles;
}
- // changeToFontSize = (mark: Mark, view: EditorView) => {
- // const size = mark.attrs.fontSize;
- // if (size) { this.updateFontSizeDropdown(String(size) + " pt"); }
- // if (this.editorProps) {
- // const ruleProvider = this.editorProps.ruleProvider;
- // const heading = NumCast(this.editorProps.Document.heading);
- // if (ruleProvider && heading) {
- // ruleProvider["ruleSize_" + heading] = size;
- // }
- // }
- // this.setMark(view.state.schema.marks.pFontSize.create({ fontSize: size }), view.state, view.dispatch);
- // }
+ insertSummarizer(state: EditorState<any>, dispatch: any) {
+ if (state.selection.empty) return false;
+ const mark = state.schema.marks.summarize.create();
+ const tr = state.tr;
+ tr.addMark(state.selection.from, state.selection.to, mark);
+ const content = tr.selection.content();
+ const newNode = state.schema.nodes.summary.create({ visibility: false, text: content, textslice: content.toJSON() });
+ dispatch && dispatch(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
+ return true;
+ }
reference_node(pos: ResolvedPos<any>): ProsNode | null {
if (!this.view) return null;
@@ -265,7 +288,6 @@ export default class RichTextMenu extends AntimodeMenu {
}
render() {
- console.log("render");
const fontSizeOptions = [
{ mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize },
{ mark: schema.marks.pFontSize.create({ fontSize: 8 }), title: "Set font size", label: "8pt", command: this.changeFontSize },
@@ -296,6 +318,35 @@ 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 },
+ { node: schema.nodes.ordered_list.create({ mapStyle: "multi" }), title: "Set list type", label: "1.A", command: this.changeListType },
+ { 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)),
@@ -303,8 +354,10 @@ export default class RichTextMenu extends AntimodeMenu {
this.createButton("strikethrough", "Strikethrough", toggleMark(schema.marks.strikethrough)),
this.createButton("superscript", "Superscript", toggleMark(schema.marks.superscript)),
this.createButton("subscript", "Subscript", toggleMark(schema.marks.subscript)),
+ this.createButton("indent", "Summarize", undefined, this.insertSummarizer),
this.createMarksDropdown(this.activeFontSize, fontSizeOptions),
this.createMarksDropdown(this.activeFontFamily, fontFamilyOptions),
+ this.createNodesDropdown(this.activeListType, listTypeOptions),
];
// this._marksToDoms = new Map();