1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
import { action, IReactionDisposer, reaction } from "mobx";
import { baseKeymap } from "prosemirror-commands";
import { history, redo, undo } from "prosemirror-history";
import { keymap } from "prosemirror-keymap";
import { schema } from "prosemirror-schema-basic";
import { EditorState, Transaction } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { Opt, WAITING } from "../../fields/Field";
import { SelectionManager } from "../../util/SelectionManager";
import "./FormattedTextBox.scss";
import React = require("react")
import { RichTextField } from "../../fields/RichTextField";
import { FieldViewProps, FieldView } from "./FieldView";
import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
// FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document
//
// HTML Markup: <FormattedTextBox Doc={Document's ID} FieldKey={Key's name + "Key"}
//
// In Code, the node's HTML is specified in the document's parameterized structure as:
// document.SetField(KeyStore.Layout, "<FormattedTextBox doc={doc} fieldKey={<KEYNAME>Key} />");
// and the node's binding to the specified document KEYNAME as:
// document.SetField(KeyStore.LayoutKeys, new ListField([KeyStore.<KEYNAME>]));
// The Jsx parser at run time will bind:
// 'fieldKey' property to the Key stored in LayoutKeys
// and 'doc' property to the document that is being rendered
//
// When rendered() by React, this extracts the TextController from the Document stored at the
// specified Key and assigns it to an HTML input node. When changes are made tot his node,
// this will edit the document and assign the new value to that field.
//
export class FormattedTextBox extends React.Component<FieldViewProps> {
public static LayoutString() { return FieldView.LayoutString("FormattedTextBox"); }
private _ref: React.RefObject<HTMLDivElement>;
private _editorView: Opt<EditorView>;
private _reactionDisposer: Opt<IReactionDisposer>;
constructor(props: FieldViewProps) {
super(props);
this._ref = React.createRef();
this.onChange = this.onChange.bind(this);
}
dispatchTransaction = (tx: Transaction) => {
if (this._editorView && this._editorView != WAITING) {
const state = this._editorView.state.apply(tx);
this._editorView.updateState(state);
const { doc, fieldKey } = this.props;
doc.SetFieldValue(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
}
}
componentDidMount() {
let state: EditorState;
const { doc, fieldKey } = this.props;
const config = {
schema,
plugins: [
history(),
keymap({ "Mod-z": undo, "Mod-y": redo }),
keymap(baseKeymap)
]
};
let field = doc.GetFieldT(fieldKey, RichTextField);
if (field && field != WAITING) { // bcz: don't think this works
state = EditorState.fromJSON(config, JSON.parse(field.Data));
} else {
state = EditorState.create(config);
}
if (this._ref.current) {
this._editorView = new EditorView(this._ref.current, {
state,
dispatchTransaction: this.dispatchTransaction
});
}
this._reactionDisposer = reaction(() => {
const field = this.props.doc.GetFieldT(this.props.fieldKey, RichTextField);
return field && field != WAITING ? field.Data : undefined;
}, (field) => {
if (field && this._editorView && this._editorView != WAITING) {
this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field)));
}
})
}
componentWillUnmount() {
if (this._editorView && this._editorView != WAITING) {
this._editorView.destroy();
}
if (this._reactionDisposer && this._reactionDisposer != WAITING) {
this._reactionDisposer();
}
}
shouldComponentUpdate() {
return false;
}
@action
onChange(e: React.ChangeEvent<HTMLInputElement>) {
const { fieldKey, doc } = this.props;
doc.SetFieldValue(fieldKey, e.target.value, RichTextField);
}
onPointerDown = (e: React.PointerEvent): void => {
let me = this;
if (e.buttons === 1 && me.props.DocumentViewForField instanceof CollectionFreeFormDocumentView && SelectionManager.IsSelected(me.props.DocumentViewForField)) {
e.stopPropagation();
}
}
render() {
return (<div className="formattedTextBox-cont"
style={{
color: "initial",
whiteSpace: "initial"
}}
onPointerDown={this.onPointerDown}
ref={this._ref} />)
}
}
|