aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/FormattedTextBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/FormattedTextBox.tsx')
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx146
1 files changed, 96 insertions, 50 deletions
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index a49497f8f..41ee24498 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1,23 +1,27 @@
-import { action, IReactionDisposer, reaction } from "mobx";
+import { action, IReactionDisposer, reaction, trace, computed, _allowStateChangesInsideComputed } from "mobx";
import { baseKeymap } from "prosemirror-commands";
-import { history, redo, undo } from "prosemirror-history";
+import { history } from "prosemirror-history";
import { keymap } from "prosemirror-keymap";
import { EditorState, Plugin, Transaction } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { FieldWaiting, Opt } from "../../../fields/Field";
+import { KeyStore } from "../../../fields/KeyStore";
import { RichTextField } from "../../../fields/RichTextField";
+import { TextField } from "../../../fields/TextField";
+import { Document } from "../../../fields/Document";
+import buildKeymap from "../../util/ProsemirrorKeymap";
import { inpRules } from "../../util/RichTextRules";
import { schema } from "../../util/RichTextSchema";
+import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu";
import { TooltipTextMenu } from "../../util/TooltipTextMenu";
import { ContextMenu } from "../../views/ContextMenu";
-import { Main } from "../Main";
+import { MainOverlayTextBox } from "../MainOverlayTextBox";
import { FieldView, FieldViewProps } from "./FieldView";
import "./FormattedTextBox.scss";
import React = require("react");
-import { TextField } from "../../../fields/TextField";
-import { KeyStore } from "../../../fields/KeyStore";
-const { buildMenuItems } = require("prosemirror-example-setup");
-const { menuBar } = require("prosemirror-menu");
+import { SelectionManager } from "../../util/SelectionManager";
+import { observer } from "mobx-react";
+import { InkingControl } from "../InkingControl";
// FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document
//
@@ -40,12 +44,15 @@ export interface FormattedTextBoxOverlay {
isOverlay?: boolean;
}
+@observer
export class FormattedTextBox extends React.Component<(FieldViewProps & FormattedTextBoxOverlay)> {
public static LayoutString(fieldStr: string = "DataKey") {
return FieldView.LayoutString(FormattedTextBox, fieldStr);
}
private _ref: React.RefObject<HTMLDivElement>;
+ private _proseRef: React.RefObject<HTMLDivElement>;
private _editorView: Opt<EditorView>;
+ private _gotDown: boolean = false;
private _reactionDisposer: Opt<IReactionDisposer>;
private _inputReactionDisposer: Opt<IReactionDisposer>;
private _proxyReactionDisposer: Opt<IReactionDisposer>;
@@ -54,14 +61,16 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
super(props);
this._ref = React.createRef();
+ this._proseRef = React.createRef();
this.onChange = this.onChange.bind(this);
}
_applyingChange: boolean = false;
+ _lastState: any = undefined;
dispatchTransaction = (tx: Transaction) => {
if (this._editorView) {
- const state = this._editorView.state.apply(tx);
+ const state = this._lastState = this._editorView.state.apply(tx);
this._editorView.updateState(state);
this._applyingChange = true;
this.props.Document.SetDataOnPrototype(
@@ -71,7 +80,11 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
);
this.props.Document.SetDataOnPrototype(KeyStore.DocumentText, state.doc.textBetween(0, state.doc.content.size, "\n\n"), TextField);
this._applyingChange = false;
- // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
+ if (this.props.Document.Title.startsWith("-") && this._editorView) {
+ let str = this._editorView.state.doc.textContent;
+ let titlestr = str.substr(0, Math.min(40, str.length));
+ this.props.Document.SetText(KeyStore.Title, "-" + titlestr + (str.length > 40 ? "..." : ""));
+ };
}
}
@@ -80,59 +93,54 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
schema,
inpRules, //these currently don't do anything, but could eventually be helpful
plugins: this.props.isOverlay ? [
+ this.tooltipTextMenuPlugin(),
history(),
- keymap({ "Mod-z": undo, "Mod-y": redo }),
+ keymap(buildKeymap(schema)),
keymap(baseKeymap),
- this.tooltipMenuPlugin()
+ // this.tooltipLinkingMenuPlugin(),
+ new Plugin({
+ props: {
+ attributes: { class: "ProseMirror-example-setup-style" }
+ }
+ })
] : [
history(),
- keymap({ "Mod-z": undo, "Mod-y": redo }),
+ keymap(buildKeymap(schema)),
keymap(baseKeymap),
]
};
if (this.props.isOverlay) {
- this._inputReactionDisposer = reaction(() => Main.Instance._textDoc && Main.Instance._textDoc.Id,
+ this._inputReactionDisposer = reaction(() => MainOverlayTextBox.Instance.TextDoc && MainOverlayTextBox.Instance.TextDoc.Id,
() => {
if (this._editorView) {
this._editorView.destroy();
}
-
- this.setupEditor(config);
+ this.setupEditor(config, this.props.Document);// MainOverlayTextBox.Instance.TextDoc); // bcz: not sure why, but the order of events is such that this.props.Document hasn't updated yet, so without forcing the editor to the MainOverlayTextBox, it will display the previously focused textbox
}
);
} else {
this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
- () => this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform()));
+ () => this.props.isSelected() && MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform));
}
+
this._reactionDisposer = reaction(
() => {
const field = this.props.Document ? this.props.Document.GetT(this.props.fieldKey, RichTextField) : undefined;
return field && field !== FieldWaiting ? field.Data : undefined;
},
- field => {
- if (field && this._editorView && !this._applyingChange) {
- this._editorView.updateState(
- EditorState.fromJSON(config, JSON.parse(field))
- );
- }
- }
+ field => field && this._editorView && !this._applyingChange &&
+ this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field)))
);
- this.setupEditor(config);
+ this.setupEditor(config, this.props.Document);
}
- private setupEditor(config: any) {
- let state: EditorState;
- let field = this.props.Document ? this.props.Document.GetT(this.props.fieldKey, RichTextField) : undefined;
- if (field && field !== FieldWaiting && field.Data) {
- state = EditorState.fromJSON(config, JSON.parse(field.Data));
- } else {
- state = EditorState.create(config);
- }
+ private setupEditor(config: any, doc?: Document) {
+ let field = doc ? doc.GetT(this.props.fieldKey, RichTextField) : undefined;
if (this._ref.current) {
this._editorView = new EditorView(this._ref.current, {
- state,
+ state: field && field.Data ? EditorState.fromJSON(config, JSON.parse(field.Data)) : EditorState.create(config),
dispatchTransaction: this.dispatchTransaction
});
}
@@ -158,25 +166,27 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
}
}
- shouldComponentUpdate() {
- return false;
- }
-
@action
onChange(e: React.ChangeEvent<HTMLInputElement>) {
const { fieldKey, Document } = this.props;
Document.SetOnPrototype(fieldKey, new RichTextField(e.target.value));
// doc.SetData(fieldKey, e.target.value, RichTextField);
}
+ @action
onPointerDown = (e: React.PointerEvent): void => {
- if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
+ if (e.button === 0 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
e.stopPropagation();
+ if (this._toolTipTextMenu && this._toolTipTextMenu.tooltip)
+ this._toolTipTextMenu.tooltip.style.opacity = "0";
}
- if (e.button === 2) {
+ if (e.button === 2 || (e.button === 0 && e.ctrlKey)) {
+ this._gotDown = true;
e.preventDefault();
}
}
onPointerUp = (e: React.PointerEvent): void => {
+ if (this._toolTipTextMenu && this._toolTipTextMenu.tooltip)
+ this._toolTipTextMenu.tooltip.style.opacity = "1";
if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
e.stopPropagation();
}
@@ -184,10 +194,10 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
onFocused = (e: React.FocusEvent): void => {
if (!this.props.isOverlay) {
- Main.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform());
+ MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform);
} else {
if (this._ref.current) {
- this._ref.current.scrollTop = Main.Instance._textScroll;
+ this._ref.current.scrollTop = MainOverlayTextBox.Instance.TextScroll;
}
}
}
@@ -196,6 +206,10 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
textCapability = (e: React.MouseEvent): void => { };
specificContextMenu = (e: React.MouseEvent): void => {
+ if (!this._gotDown) {
+ e.preventDefault();
+ return;
+ }
ContextMenu.Instance.addItem({
description: "Text Capability",
event: this.textCapability
@@ -216,37 +230,69 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
}
onPointerWheel = (e: React.WheelEvent): void => {
- e.stopPropagation();
+ if (this.props.isSelected()) {
+ e.stopPropagation();
+ }
+ }
+
+ onClick = (e: React.MouseEvent): void => {
+ this._proseRef.current!.focus();
}
- tooltipMenuPlugin() {
+ tooltipTextMenuPlugin() {
let myprops = this.props;
+ let self = this;
return new Plugin({
view(_editorView) {
- return new TooltipTextMenu(_editorView, myprops);
+ return self._toolTipTextMenu = new TooltipTextMenu(_editorView, myprops);
}
});
}
- onKeyPress(e: React.KeyboardEvent) {
+
+ _toolTipTextMenu: TooltipTextMenu | undefined = undefined;
+ tooltipLinkingMenuPlugin() {
+ let myprops = this.props;
+ return new Plugin({
+ view(_editorView) {
+ return new TooltipLinkingMenu(_editorView, myprops);
+ }
+ });
+ }
+
+ @action
+ onKeyPress = (e: React.KeyboardEvent) => {
+ if (e.key == "Escape") {
+ SelectionManager.DeselectAll();
+ }
e.stopPropagation();
+ if (e.key === "Tab") e.preventDefault();
// stop propagation doesn't seem to stop propagation of native keyboard events.
// so we set a flag on the native event that marks that the event's been handled.
- // (e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
+ (e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
}
render() {
+ let style = this.props.isOverlay ? "-scroll" : "-hidden";
+ let rounded = this.props.Document.GetNumber(KeyStore.BorderRounding, 0) < 0 ? "-rounded" : "";
+ let color = this.props.Document.GetText(KeyStore.BackgroundColor, "");
+ let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive";
return (
- <div
- className={`formattedTextBox-cont`}
+ <div className={`formattedTextBox-cont${style}`} ref={this._ref}
+ style={{
+ pointerEvents: interactive ? "all" : "none",
+ background: color,
+ }}
onKeyDown={this.onKeyPress}
onKeyPress={this.onKeyPress}
onFocus={this.onFocused}
+ onClick={this.onClick}
onPointerUp={this.onPointerUp}
onPointerDown={this.onPointerDown}
onContextMenu={this.specificContextMenu}
// tfs: do we need this event handler
onWheel={this.onPointerWheel}
- ref={this._ref}
- />
+ >
+ <div className={`formattedTextBox-inner${rounded}`} ref={this._proseRef} />
+ </div>
);
}
}