aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/DocumentView.tsx3
-rw-r--r--src/client/views/nodes/FontIconBox.tsx9
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx41
-rw-r--r--src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts24
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx177
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts3
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts2
7 files changed, 186 insertions, 73 deletions
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 21b6d8310..09eeaee36 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1169,8 +1169,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
render() {
- if (this.props.Document[AclSym] && this.props.Document[AclSym] === AclPrivate) return (null);
if (!(this.props.Document instanceof Doc)) return (null);
+ if (this.props.Document[AclSym] && this.props.Document[AclSym] === AclPrivate) return (null);
+ if (this.props.Document.hidden) return (null);
const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : this.props.forcedBackgroundColor?.(this.Document) || StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document);
const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null)));
const finalOpacity = this.props.opacity ? this.props.opacity() : opacity;
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index cf0b16c7c..5e8dd2497 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -5,7 +5,7 @@ import { createSchema, makeInterface } from '../../../fields/Schema';
import { DocComponent } from '../DocComponent';
import './FontIconBox.scss';
import { FieldView, FieldViewProps } from './FieldView';
-import { StrCast, Cast } from '../../../fields/Types';
+import { StrCast, Cast, NumCast } from '../../../fields/Types';
import { Utils } from "../../../Utils";
import { runInAction, observable, reaction, IReactionDisposer } from 'mobx';
import { Doc } from '../../../fields/Doc';
@@ -59,13 +59,14 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
render() {
const referenceDoc = (this.layoutDoc.dragFactory instanceof Doc ? this.layoutDoc.dragFactory : this.layoutDoc);
- const referenceLayout = Doc.Layout(referenceDoc);
+ const refLayout = Doc.Layout(referenceDoc);
return <button className="fontIconBox-outerDiv" title={StrCast(this.layoutDoc.title)} ref={this._ref} onContextMenu={this.specificContextMenu}
style={{
- background: StrCast(referenceLayout.backgroundColor),
+ padding: Cast(this.layoutDoc._xPadding, "number", null),
+ background: StrCast(refLayout._backgroundColor, StrCast(refLayout.backgroundColor)),
boxShadow: this.layoutDoc.ischecked ? `4px 4px 12px black` : undefined
}}>
- <FontAwesomeIcon className="fontIconBox-icon" icon={this.dataDoc.icon as any} color={this._foregroundColor} size="sm" />
+ <FontAwesomeIcon className="fontIconBox-icon" icon={this.dataDoc.icon as any} color={StrCast(this.layoutDoc.color, this._foregroundColor)} size="sm" />
{!this.rootDoc.label ? (null) : <div className="fontIconBox-label"> {StrCast(this.rootDoc.label).substring(0, 5)} </div>}
</button>;
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2cb55e0fa..2a79e2f13 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -32,7 +32,7 @@ import { DocumentType } from '../../../documents/DocumentTypes';
import { DictationManager } from '../../../util/DictationManager';
import { DragManager } from "../../../util/DragManager";
import { makeTemplate } from '../../../util/DropConverter';
-import buildKeymap from "./ProsemirrorExampleTransfer";
+import buildKeymap, { updateBullets } from "./ProsemirrorExampleTransfer";
import RichTextMenu from './RichTextMenu';
import { RichTextRules } from "./RichTextRules";
@@ -86,6 +86,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
public static blankState = () => EditorState.create(FormattedTextBox.Instance.config);
public static Instance: FormattedTextBox;
public ProseRef?: HTMLDivElement;
+ public get EditorView() { return this._editorView; }
private _ref: React.RefObject<HTMLDivElement> = React.createRef();
private _scrollRef: React.RefObject<HTMLDivElement> = React.createRef();
private _editorView: Opt<EditorView>;
@@ -93,7 +94,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
private _searchIndex = 0;
private _undoTyping?: UndoManager.Batch;
private _disposers: { [name: string]: IReactionDisposer } = {};
- private dropDisposer?: DragManager.DragDropDisposer;
+ private _dropDisposer?: DragManager.DragDropDisposer;
@computed get _recording() { return this.dataDoc.audioState === "recording"; }
set _recording(value) { this.dataDoc.audioState = value ? "recording" : undefined; }
@@ -291,8 +292,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
protected createDropTarget = (ele: HTMLDivElement) => {
this.ProseRef = ele;
- this.dropDisposer?.();
- ele && (this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc));
+ this._dropDisposer?.();
+ ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc));
}
@undoBatch
@@ -660,8 +661,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
incomingValue => {
if (incomingValue !== undefined && this._editorView && !this._applyingChange) {
const updatedState = JSON.parse(incomingValue);
- this._editorView.updateState(EditorState.fromJSON(this.config, updatedState));
- this.tryUpdateHeight();
+ if (JSON.stringify(this._editorView!.state.toJSON()) !== JSON.stringify(updatedState)) {
+ this._editorView.updateState(EditorState.fromJSON(this.config, updatedState));
+ this.tryUpdateHeight();
+ }
}
}
);
@@ -1046,6 +1049,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
@action
onFocused = (e: React.FocusEvent): void => {
+ console.log("FOUCSS")
FormattedTextBox.FocusedBox = this;
this.tryUpdateHeight();
@@ -1163,18 +1167,28 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
});
}
- public static HadSelection: boolean = false;
- onBlur = (e: any) => {
- FormattedTextBox.HadSelection = window.getSelection()?.toString() !== "";
- //DictationManager.Controls.stop(false);
+ public startUndoTypingBatch() {
+ this._undoTyping = UndoManager.StartBatch("undoTyping");
+ }
+
+ public endUndoTypingBatch() {
+ const wasUndoing = this._undoTyping;
if (this._undoTyping) {
this._undoTyping.end();
this._undoTyping = undefined;
}
+ return wasUndoing;
+ }
+ public static HadSelection: boolean = false;
+ onBlur = (e: any) => {
+ console.log("BLURRR")
+ FormattedTextBox.HadSelection = window.getSelection()?.toString() !== "";
+ //DictationManager.Controls.stop(false);
+ this.endUndoTypingBatch();
this.doLinkOnDeselect();
// move the richtextmenu offscreen
- if (!RichTextMenu.Instance.Pinned && !RichTextMenu.Instance.overMenu) RichTextMenu.Instance.jumpTo(-300, -300);
+ if (!RichTextMenu.Instance.Pinned) RichTextMenu.Instance.delayHide();
}
_lastTimedMark: Mark | undefined = undefined;
@@ -1208,11 +1222,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
// this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(mark));
if (!this._undoTyping) {
- this._undoTyping = UndoManager.StartBatch("undoTyping");
+ this.startUndoTypingBatch();
}
}
ondrop = (eve: React.DragEvent) => {
+ this._editorView!.dispatch(updateBullets(this._editorView!.state.tr, this._editorView!.state.schema));
eve.stopPropagation(); // drag n drop of text within text note will generate a new note if not caughst, as will dragging in from outside of Dash.
}
onscrolled = (ev: React.UIEvent) => {
@@ -1308,7 +1323,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
onScroll={this.onscrolled} onDrop={this.ondrop} >
<div className={`formattedTextBox-inner${rounded}${selclass}`} ref={this.createDropTarget}
style={{
- padding: this.layoutDoc._textBoxPadding ? this.layoutDoc._textBoxPadding : `${Math.max(0, NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0) + selPad)}px ${NumCast(this.layoutDoc._xMargin, this.props.xMargin || 0) + selPad}px`,
+ padding: this.layoutDoc._textBoxPadding ? StrCast(this.layoutDoc._textBoxPadding) : `${Math.max(0, NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0) + selPad)}px ${NumCast(this.layoutDoc._xMargin, this.props.xMargin || 0) + selPad}px`,
pointerEvents: !this.props.isSelected() ? ((this.layoutDoc.isLinkButton || this.props.onClick) ? "none" : "all") : undefined
}}
/>
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 1bbcb9fa8..9d69f4be7 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -1,7 +1,6 @@
import { chainCommands, exitCode, joinDown, joinUp, lift, deleteSelection, joinBackward, selectNodeBackward, setBlockType, splitBlockKeepMarks, toggleMark, wrapIn, newlineInCode } from "prosemirror-commands";
import { liftTarget } from "prosemirror-transform";
import { redo, undo } from "prosemirror-history";
-import { undoInputRule } from "prosemirror-inputrules";
import { Schema } from "prosemirror-model";
import { liftListItem, sinkListItem } from "./prosemirrorPatches.js";
import { splitListItem, wrapInList, } from "prosemirror-schema-list";
@@ -12,7 +11,6 @@ import { Doc, DataSym } from "../../../../fields/Doc";
import { FormattedTextBox } from "./FormattedTextBox";
import { Id } from "../../../../fields/FieldSymbols";
import { Docs } from "../../../documents/Documents";
-import { update } from "lodash";
const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
@@ -215,10 +213,13 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
marks && tx3.setStoredMarks([...marks]);
dispatch(tx3);
})) {
+ const fromattrs = state.selection.$from.node().attrs;
if (!splitBlockKeepMarks(state, (tx3: Transaction) => {
- splitMetadata(marks, tx3);
- if (!liftListItem(schema.nodes.list_item)(tx3, dispatch as ((tx: Transaction<Schema<any, any>>) => void))) {
- dispatch(tx3);
+ const tonode = tx3.selection.$to.node();
+ const tx4 = tx3.setNodeMarkup(tx3.selection.to - 1, tonode.type, fromattrs, tonode.marks);
+ splitMetadata(marks, tx4);
+ if (!liftListItem(schema.nodes.list_item)(tx4, dispatch as ((tx: Transaction<Schema<any, any>>) => void))) {
+ dispatch(tx4);
}
})) {
return false;
@@ -281,19 +282,6 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
return false;
});
- // bind("^", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
- // let newNode = schema.nodes.footnote.create({});
- // if (dispatch && state.selection.from === state.selection.to) {
- // let tr = state.tr;
- // tr.replaceSelectionWith(newNode); // replace insertion with a footnote.
- // dispatch(tr.setSelection(new NodeSelection( // select the footnote node to open its display
- // tr.doc.resolve( // get the location of the footnote node by subtracting the nodesize of the footnote from the current insertion point anchor (which will be immediately after the footnote node)
- // tr.selection.anchor - tr.selection.$anchor.nodeBefore!.nodeSize))));
- // return true;
- // }
- // return false;
- // });
-
return keys;
}
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 839943aac..6fdcaceda 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -1,8 +1,8 @@
import React = require("react");
import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faBold, faCaretDown, faChevronLeft, faEyeDropper, faHighlighter, faIndent, faItalic, faLink, faPaintRoller, faPalette, faStrikethrough, faSubscript, faSuperscript, faUnderline } from "@fortawesome/free-solid-svg-icons";
+import { faBold, faCaretDown, faChevronLeft, faEyeDropper, faHighlighter, faOutdent, faIndent, faHandPointLeft, faHandPointRight, faItalic, faLink, faPaintRoller, faPalette, faStrikethrough, faSubscript, faSuperscript, faUnderline } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable } from "mobx";
+import { action, observable, IReactionDisposer, reaction } from "mobx";
import { observer } from "mobx-react";
import { lift, wrapIn } from "prosemirror-commands";
import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos } from "prosemirror-model";
@@ -23,9 +23,10 @@ import { updateBullets } from "./ProsemirrorExampleTransfer";
import "./RichTextMenu.scss";
import { schema } from "./schema_rts";
import { TraceMobx } from "../../../../fields/util";
+import { UndoManager } from "../../../util/UndoManager";
const { toggleMark } = require("prosemirror-commands");
-library.add(faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, faCaretDown, faPalette, faHighlighter, faLink, faPaintRoller);
+library.add(faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faOutdent, faIndent, faHandPointLeft, faHandPointRight, faEyeDropper, faCaretDown, faPalette, faHighlighter, faLink, faPaintRoller);
@observer
@@ -68,6 +69,8 @@ export default class RichTextMenu extends AntimodeMenu {
@observable private currentLink: string | undefined = "";
@observable private showLinkDropdown: boolean = false;
+ _reaction: IReactionDisposer | undefined;
+ _delayHide = false;
constructor(props: Readonly<{}>) {
super(props);
RichTextMenu.Instance = this;
@@ -138,6 +141,16 @@ export default class RichTextMenu extends AntimodeMenu {
];
}
+ componentDidMount() {
+ this._reaction = reaction(() => SelectionManager.SelectedDocuments(),
+ () => this._delayHide && !(this._delayHide = false) && this.fadeOut(true));
+ }
+ componentWillUnmount() {
+ this._reaction?.();
+ }
+
+ public delayHide = () => { this._delayHide = true; }
+
@action
changeView(view: EditorView) {
this.view = view;
@@ -147,16 +160,6 @@ export default class RichTextMenu extends AntimodeMenu {
this.updateFromDash(view, lastState, this.editorProps);
}
- public MakeLinkToSelection = (linkDocId: string, title: string, location: string, targetDocId: string): string => {
- if (this.view) {
- const link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + linkDocId), title: title, location: location, linkId: linkDocId, targetId: targetDocId });
- this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link).
- addMark(this.view.state.selection.from, this.view.state.selection.to, link));
- return this.view.state.selection.$from.nodeAfter?.text || "";
- }
- return "";
- }
-
@action
public async updateFromDash(view: EditorView, lastState: EditorState | undefined, props: any) {
if (!view) {
@@ -310,8 +313,11 @@ export default class RichTextMenu extends AntimodeMenu {
function onClick(e: React.PointerEvent) {
e.preventDefault();
e.stopPropagation();
- self.view && command && command(self.view.state, self.view.dispatch, self.view);
- self.view && onclick && onclick(self.view.state, self.view.dispatch, self.view);
+ self.TextView.endUndoTypingBatch();
+ UndoManager.RunInBatch(() => {
+ self.view && command && command(self.view.state, self.view.dispatch, self.view);
+ self.view && onclick && onclick(self.view.state, self.view.dispatch, self.view);
+ }, "rich text menu command");
self.setActiveMarkButtons(self.getActiveMarksOnSelection());
}
@@ -338,9 +344,10 @@ export default class RichTextMenu extends AntimodeMenu {
function onChange(e: React.ChangeEvent<HTMLSelectElement>) {
e.stopPropagation();
e.preventDefault();
+ self.TextView.endUndoTypingBatch();
options.forEach(({ label, mark, command }) => {
if (e.target.value === label) {
- self.view && mark && command(mark, self.view);
+ UndoManager.RunInBatch(() => self.view && mark && command(mark, self.view), "text mark dropdown");
}
});
}
@@ -361,9 +368,10 @@ export default class RichTextMenu extends AntimodeMenu {
const self = this;
function onChange(val: string) {
+ self.TextView.endUndoTypingBatch();
options.forEach(({ label, node, command }) => {
if (val === label) {
- self.view && node && command(node);
+ UndoManager.RunInBatch(() => self.view && node && command(node), "nodes dropdown");
}
});
}
@@ -412,6 +420,85 @@ export default class RichTextMenu extends AntimodeMenu {
dispatch?.(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
return true;
}
+ alignCenter = (state: EditorState<any>, dispatch: any) => {
+ return this.alignParagraphs(state, "center", dispatch);
+ }
+ alignLeft = (state: EditorState<any>, dispatch: any) => {
+ return this.alignParagraphs(state, "left", dispatch);
+ }
+ alignRight = (state: EditorState<any>, dispatch: any) => {
+ return this.alignParagraphs(state, "right", dispatch);
+ }
+
+ alignParagraphs(state: EditorState<any>, align: "left" | "right" | "center", dispatch: any) {
+ var tr = state.tr;
+ state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => {
+ if (node.type === schema.nodes.paragraph) {
+ tr = tr.setNodeMarkup(pos, node.type, { ...node.attrs, align }, node.marks);
+ return false;
+ }
+ return true;
+ });
+ dispatch?.(tr);
+ return true;
+ }
+
+ insetParagraph(state: EditorState<any>, dispatch: any) {
+ var tr = state.tr;
+ state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => {
+ if (node.type === schema.nodes.paragraph) {
+ const inset = (node.attrs.inset ? Number(node.attrs.inset) : 0) + 10;
+ tr = tr.setNodeMarkup(pos, node.type, { ...node.attrs, inset }, node.marks);
+ return false;
+ }
+ return true;
+ });
+ dispatch?.(tr);
+ return true;
+ }
+ outsetParagraph(state: EditorState<any>, dispatch: any) {
+ var tr = state.tr;
+ state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => {
+ if (node.type === schema.nodes.paragraph) {
+ const inset = Math.max(0, (node.attrs.inset ? Number(node.attrs.inset) : 0) - 10);
+ tr = tr.setNodeMarkup(pos, node.type, { ...node.attrs, inset }, node.marks);
+ return false;
+ }
+ return true;
+ });
+ dispatch?.(tr);
+ return true;
+ }
+
+ indentParagraph(state: EditorState<any>, dispatch: any) {
+ var tr = state.tr;
+ state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => {
+ if (node.type === schema.nodes.paragraph) {
+ const nodeval = node.attrs.indent ? Number(node.attrs.indent) : undefined;
+ const indent = !nodeval ? 25 : nodeval < 0 ? 0 : nodeval + 25;
+ tr = tr.setNodeMarkup(pos, node.type, { ...node.attrs, indent }, node.marks);
+ return false;
+ }
+ return true;
+ });
+ dispatch?.(tr);
+ return true;
+ }
+
+ hangingIndentParagraph(state: EditorState<any>, dispatch: any) {
+ var tr = state.tr;
+ state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => {
+ if (node.type === schema.nodes.paragraph) {
+ const nodeval = node.attrs.indent ? Number(node.attrs.indent) : undefined;
+ const indent = !nodeval ? -25 : nodeval > 0 ? 0 : nodeval - 10;
+ tr = tr.setNodeMarkup(pos, node.type, { ...node.attrs, indent }, node.marks);
+ return false;
+ }
+ return true;
+ });
+ dispatch?.(tr);
+ return true;
+ }
insertBlockquote(state: EditorState<any>, dispatch: any) {
const path = (state.selection.$from as any).path;
@@ -423,6 +510,11 @@ export default class RichTextMenu extends AntimodeMenu {
return true;
}
+ insertHorizontalRule(state: EditorState<any>, dispatch: any) {
+ dispatch(state.tr.replaceSelectionWith(state.schema.nodes.horizontal_rule.create()).scrollIntoView());
+ return true;
+ }
+
@action toggleBrushDropdown() { this.showBrushDropdown = !this.showBrushDropdown; }
// todo: add brushes to brushMap to save with a style name
@@ -439,7 +531,8 @@ export default class RichTextMenu extends AntimodeMenu {
function onBrushClick(e: React.PointerEvent) {
e.preventDefault();
e.stopPropagation();
- self.view && self.fillBrush(self.view.state, self.view.dispatch);
+ self.TextView.endUndoTypingBatch();
+ UndoManager.RunInBatch(() => self.view && self.fillBrush(self.view.state, self.view.dispatch), "rt brush");
}
let label = "Stored marks: ";
@@ -506,19 +599,24 @@ export default class RichTextMenu extends AntimodeMenu {
@action toggleColorDropdown() { this.showColorDropdown = !this.showColorDropdown; }
@action setActiveColor(color: string) { this.activeFontColor = color; }
+ get TextView() { return (this.view as any).TextView as FormattedTextBox }
createColorButton() {
const self = this;
function onColorClick(e: React.PointerEvent) {
e.preventDefault();
e.stopPropagation();
- self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch);
+ self.TextView.endUndoTypingBatch();
+ UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color");
+ self.TextView.EditorView!.focus()
}
function changeColor(e: React.PointerEvent, color: string) {
e.preventDefault();
e.stopPropagation();
self.setActiveColor(color);
- self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch);
+ self.TextView.endUndoTypingBatch();
+ UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color");
+ self.TextView.EditorView!.focus()
}
const button =
@@ -563,13 +661,15 @@ export default class RichTextMenu extends AntimodeMenu {
function onHighlightClick(e: React.PointerEvent) {
e.preventDefault();
e.stopPropagation();
- self.view && self.insertHighlight(self.activeHighlightColor, self.view.state, self.view.dispatch);
+ self.TextView.endUndoTypingBatch();
+ UndoManager.RunInBatch(() => self.view && self.insertHighlight(self.activeHighlightColor, self.view.state, self.view.dispatch), "rt highligher");
}
function changeHighlight(e: React.PointerEvent, color: string) {
e.preventDefault();
e.stopPropagation();
self.setActiveHighlight(color);
- self.view && self.insertHighlight(self.activeHighlightColor, self.view.state, self.view.dispatch);
+ self.TextView.endUndoTypingBatch();
+ UndoManager.RunInBatch(() => self.view && self.insertHighlight(self.activeHighlightColor, self.view.state, self.view.dispatch), "rt highlighter");
}
const button =
@@ -609,7 +709,8 @@ export default class RichTextMenu extends AntimodeMenu {
const self = this;
function onLinkChange(e: React.ChangeEvent<HTMLInputElement>) {
- self.setCurrentLink(e.target.value);
+ self.TextView.endUndoTypingBatch();
+ UndoManager.RunInBatch(() => self.setCurrentLink(e.target.value), "link change");
}
const link = this.currentLink ? this.currentLink : "";
@@ -744,7 +845,7 @@ export default class RichTextMenu extends AntimodeMenu {
return ref_node;
}
- @action onPointerEnter(e: React.PointerEvent) { RichTextMenu.Instance.overMenu = true; }
+ @action onPointerEnter(e: React.PointerEvent) { RichTextMenu.Instance.overMenu = false; }
@action onPointerLeave(e: React.PointerEvent) { RichTextMenu.Instance.overMenu = false; }
@action
@@ -768,18 +869,25 @@ export default class RichTextMenu extends AntimodeMenu {
TraceMobx();
const row1 = <div className="antimodeMenu-row" key="row1" style={{ display: this.collapsed ? "none" : undefined }}>{[
!this.collapsed ? this.getDragger() : (null),
- this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)),
- this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)),
- this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)),
- this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)),
- this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)),
- this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)),
+ !this.Pinned ? (null) : <> {[
+ this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)),
+ this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)),
+ this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)),
+ this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)),
+ this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)),
+ this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)),
+ ]}</>,
this.createColorButton(),
this.createHighlighterButton(),
this.createLinkButton(),
this.createBrushButton(),
- this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer),
- this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote),
+ this.createButton("align-left", "Align Left", undefined, this.alignLeft),
+ this.createButton("align-center", "Align Center", undefined, this.alignCenter),
+ this.createButton("align-right", "Align Right", undefined, this.alignRight),
+ this.createButton("indent", "Inset More", undefined, this.insetParagraph),
+ this.createButton("outdent", "Inset Less", undefined, this.outsetParagraph),
+ this.createButton("hand-point-left", "Hanging Indent", undefined, this.hangingIndentParagraph),
+ this.createButton("hand-point-right", "Indent", undefined, this.indentParagraph),
]}</div>;
const row2 = <div className="antimodeMenu-row row-2" key="antimodemenu row2">
@@ -787,7 +895,10 @@ export default class RichTextMenu extends AntimodeMenu {
<div key="row" style={{ display: this.collapsed ? "none" : undefined }}>
{[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size"),
this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family"),
- this.createNodesDropdown(this.activeListType, this.listTypeOptions, "nodes")]}
+ this.createNodesDropdown(this.activeListType, this.listTypeOptions, "nodes"),
+ this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer),
+ this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote),
+ this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule),]}
</div>
<div key="button">
{/* <div key="collapser">
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 6a85e3b7c..612a824fc 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -315,8 +315,6 @@ export class RichTextRules {
return node ? state.tr.replaceRangeWith(start, end, dashDoc).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
}),
-
-
// create an inline view of a tag stored under the '#' field
new InputRule(
new RegExp(/#([a-zA-Z_\-]+[a-zA-Z_;\-0-9]*)\s$/),
@@ -374,7 +372,6 @@ export class RichTextRules {
new InputRule(
new RegExp(/%\)/),
(state, match, start, end) => {
-
return state.tr.deleteRange(start, end).removeStoredMark(state.schema.marks.summarizeInclusive.create());
}),
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index afb1f57b7..f83cff9b9 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -312,7 +312,7 @@ export const nodes: { [index: string]: NodeSpec } = {
const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : "";
return node.attrs.visibility ?
["li", { class: `${map}`, "data-mapStyle": node.attrs.mapStyle, "data-bulletStyle": node.attrs.bulletStyle }, 0] :
- ["li", { class: `${map}`, "data-mapStyle": node.attrs.mapStyle, "data-bulletStyle": node.attrs.bulletStyle }, "..."];
+ ["li", { class: `${map}`, "data-mapStyle": node.attrs.mapStyle, "data-bulletStyle": node.attrs.bulletStyle }, `${node.firstChild?.textContent}...`];
}
},
}; \ No newline at end of file