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/ColorBox.tsx7
-rw-r--r--src/client/views/nodes/DocumentView.tsx53
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx2
-rw-r--r--src/client/views/nodes/ScriptingBox.tsx16
-rw-r--r--src/client/views/nodes/WebBox.tsx9
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss9
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx9
-rw-r--r--src/client/views/nodes/formattedText/ParagraphNodeSpec.ts2
-rw-r--r--src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts29
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx89
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts24
-rw-r--r--src/client/views/nodes/formattedText/prosemirrorPatches.js4
12 files changed, 162 insertions, 91 deletions
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 2e6828132..a49b4eee7 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -36,8 +36,11 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument
view.props.Document.layout instanceof Doc ? view.props.Document.layout :
view.props.Document.isTemplateForField ? view.props.Document : Doc.GetProto(view.props.Document);
if (targetDoc) {
- if (StrCast(Doc.Layout(view.props.Document).layout).indexOf("FormattedTextBox") !== -1 && FormattedTextBox.HadSelection) {
- Doc.Layout(view.props.Document).color = Doc.UserDoc().bacgroundColor;
+ if (view.props.LayoutTemplate?.() || view.props.LayoutTemplateString) { // this situation typically occurs when you have a link dot
+ targetDoc.backgroundColor = Doc.UserDoc().backgroundColor; // bcz: don't know how to change the color of an inline template...
+ }
+ else if (StrCast(Doc.Layout(view.props.Document).layout).includes("FormattedTextBox") && FormattedTextBox.HadSelection) {
+ Doc.Layout(view.props.Document)[Doc.LayoutFieldKey(view.props.Document) + "-color"] = Doc.UserDoc().backgroundColor;
} else {
Doc.Layout(view.props.Document)._backgroundColor = Doc.UserDoc().backgroundColor; // '_backgroundColor' is template specific. 'backgroundColor' would apply to all templates, but has no UI at the moment
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 707058aa7..d31ec8c1a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -91,6 +91,7 @@ export interface DocumentViewProps {
pinToPres: (document: Doc) => void;
backgroundHalo?: () => boolean;
backgroundColor?: (doc: Doc) => string | undefined;
+ forcedBackgroundColor?: (doc: Doc) => string | undefined;
opacity?: () => number | undefined;
ChromeHeight?: () => number;
dontRegisterView?: boolean;
@@ -122,7 +123,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@computed get freezeDimensions() { return this.props.FreezeDimensions; }
@computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth, this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
@computed get nativeHeight() { return NumCast(this.layoutDoc._nativeHeight, this.props.NativeHeight() || (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); }
- @computed get onClickHandler() { return this.props.onClick || Cast(this.layoutDoc.onClick, ScriptField, null) || this.Document.onClick; }
+ @computed get onClickHandler() { return this.props.onClick || Cast(this.layoutDoc.onClick, ScriptField, null); }
@computed get onDoubleClickHandler() { return this.props.onDoubleClick || Cast(this.layoutDoc.onDoubleClick, ScriptField, null) || this.Document.onDoubleClick; }
@computed get onPointerDownHandler() { return this.props.onPointerDown ? this.props.onPointerDown : this.Document.onPointerDown; }
@computed get onPointerUpHandler() { return this.props.onPointerUp ? this.props.onPointerUp : this.Document.onPointerUp; }
@@ -235,10 +236,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
dragData.offset = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
dragData.dropAction = dropAction;
dragData.removeDocument = this.props.removeDocument;
- dragData.moveDocument = this.props.moveDocument;// this.Document.onDragStart ? undefined : this.props.moveDocument;
+ dragData.moveDocument = this.props.moveDocument;// this.layoutDoc.onDragStart ? undefined : this.props.moveDocument;
dragData.dragDivName = this.props.dragDivName;
dragData.treeViewId = this.props.treeViewId;
- DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.Document.onDragStart });
+ DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.layoutDoc.onDragStart });
}
}
@@ -328,7 +329,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
} else if (this.props.Document.links && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
DocListCast(this.props.Document.links).length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey);
} else {
- if ((this.props.Document.onDragStart || (this.props.Document.rootDocument)) && !(e.ctrlKey || e.button > 0)) { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTEmplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
+ if ((this.layoutDoc.onDragStart || (this.props.Document.rootDocument)) && !(e.ctrlKey || e.button > 0)) { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTEmplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
} else {
SelectionManager.SelectDoc(this, e.ctrlKey || e.shiftKey);
@@ -374,7 +375,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this._downX = touch.clientX;
this._downY = touch.clientY;
if (!e.nativeEvent.cancelBubble) {
- if ((this.active || this.Document.onDragStart || this.Document.onClick) && !e.ctrlKey && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation();
+ if ((this.active || this.layoutDoc.onDragStart || this.layoutDoc.onClick) && !e.ctrlKey && !this.layoutDoc.lockedPosition && !this.layoutDoc.inOverlay) e.stopPropagation();
this.removeMoveListeners();
this.addMoveListeners();
this.removeEndListeners();
@@ -389,11 +390,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (e.cancelBubble && this.active) {
this.removeMoveListeners();
}
- else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.Document.onDragStart || this.Document.onClick) && !this.Document.lockedPosition && !this.Document.inOverlay) {
+ else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.layoutDoc.onDragStart || this.layoutDoc.onClick) && !this.layoutDoc.lockedPosition && !this.layoutDoc.inOverlay) {
const touch = me.touchEvent.changedTouches.item(0);
if (touch && (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3)) {
- if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick)) {
+ if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.layoutDoc.onClick)) {
this.cleanUpInteractions();
this.startDragging(this._downX, this._downY, this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined);
}
@@ -506,15 +507,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
this._downX = e.clientX;
this._downY = e.clientY;
- if ((!e.nativeEvent.cancelBubble || this.onClickHandler || this.Document.onDragStart) &&
+ if ((!e.nativeEvent.cancelBubble || this.onClickHandler || this.layoutDoc.onDragStart) &&
// if this is part of a template, let the event go up to the tempalte root unless right/ctrl clicking
!((this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0))) {
- if ((this.active || this.Document.onDragStart) &&
+ if ((this.active || this.layoutDoc.onDragStart) &&
!e.ctrlKey &&
(e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
- !this.Document.inOverlay) {
+ !this.layoutDoc.inOverlay) {
e.stopPropagation();
- if (SelectionManager.IsSelected(this, true) && this.props.Document._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it
+ if (SelectionManager.IsSelected(this, true) && this.layoutDoc._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it
}
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
@@ -532,9 +533,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (e.cancelBubble && this.active) {
document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView)
}
- else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.Document.onDragStart) && !this.Document.lockedPosition && !this.Document.inOverlay) {
+ else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.layoutDoc.onDragStart) && !this.layoutDoc.lockedPosition && !this.layoutDoc.inOverlay) {
if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
- if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
+ if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && "alias") || (this.props.dropAction || this.Document.dropAction || undefined) as dropActionType);
@@ -582,10 +583,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch
toggleLinkButtonBehavior = (): void => {
- if (this.Document.isLinkButton || this.Document.onClick || this.Document.ignoreClick) {
+ if (this.Document.isLinkButton || this.layoutDoc.onClick || this.Document.ignoreClick) {
this.Document.isLinkButton = false;
this.Document.ignoreClick = false;
- this.Document.onClick = undefined;
+ this.layoutDoc.onClick = undefined;
} else {
this.Document.isLinkButton = true;
this.Document.followLinkZoom = false;
@@ -620,6 +621,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch
@action
drop = async (e: Event, de: DragManager.DropEvent) => {
+ if (this.props.Document === Doc.UserDoc().activeWorkspace) {
+ alert("linking to document tabs not yet supported. Drop link on document content.");
+ return;
+ }
if (de.complete.annoDragData) {
/// this whole section for handling PDF annotations looks weird. Need to rethink this to make it cleaner
e.stopPropagation();
@@ -740,20 +745,20 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const existingOnClick = cm?.findByDescription("OnClick...");
const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
- onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.props.Document.layoutKey}")`), icon: "window-restore" });
+ onClicks.push({ description: "Toggle Detail", event: () => this.layoutDoc.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "window-restore" });
onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: this.toggleFollowInPlace, icon: "concierge-bell" });
onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link on Right", event: this.toggleFollowOnRight, icon: "concierge-bell" });
- onClicks.push({ description: this.Document.isLinkButton || this.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.toggleLinkButtonBehavior, icon: "concierge-bell" });
+ onClicks.push({ description: this.Document.isLinkButton || this.layoutDoc.onClick ? "Remove Click Behavior" : "Follow Link", event: this.toggleLinkButtonBehavior, icon: "concierge-bell" });
onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "edit" });
!existingOnClick && cm?.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
const funcs: ContextMenuProps[] = [];
- if (this.Document.onDragStart) {
- funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.Document.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
- funcs.push({ description: "Drag a Copy", icon: "edit", event: () => this.Document.dragFactory && (this.Document.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
- funcs.push({ description: "Drag Document", icon: "edit", event: () => this.Document.onDragStart = undefined });
- cm?.addItem({ description: "OnDrag...", subitems: funcs, icon: "asterisk" });
+ if (this.layoutDoc.onDragStart) {
+ funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
+ funcs.push({ description: "Drag a Copy", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
+ funcs.push({ description: "Drag Document", icon: "edit", event: () => this.layoutDoc.onDragStart = undefined });
+ cm.addItem({ description: "OnDrag...", subitems: funcs, icon: "asterisk" });
}
const more = cm?.findByDescription("More...");
@@ -1066,7 +1071,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
PanelWidth={this.anchorPanelWidth}
PanelHeight={this.anchorPanelHeight}
ContentScaling={returnOne}
- backgroundColor={returnTransparent}
+ forcedBackgroundColor={returnTransparent}
removeDocument={this.hideLinkAnchor}
pointerEvents={false}
LayoutTemplate={undefined}
@@ -1152,7 +1157,7 @@ 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);
- const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document);
+ 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;
const finalColor = this.layoutDoc.type === DocumentType.FONTICON || this.layoutDoc._viewType === CollectionViewType.Linear ? undefined : backgroundColor;
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 233acc481..2b64cdab6 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -116,7 +116,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
TraceMobx();
const x = this.props.PanelWidth() > 1 ? NumCast(this.rootDoc[this.fieldKey + "_x"], 100) : 0;
const y = this.props.PanelWidth() > 1 ? NumCast(this.rootDoc[this.fieldKey + "_y"], 100) : 0;
- const c = StrCast(this.layoutDoc.backgroundColor, "lightblue");
+ const c = StrCast(this.layoutDoc._backgroundColor, StrCast(this.layoutDoc.backgroundColor, StrCast(this.dataDoc.backgroundColor, "lightBlue"))); // note this is not where the typical lightBlue default color comes from. See Documents.Create.LinkDocument()
const anchor = this.fieldKey === "anchor1" ? "anchor2" : "anchor1";
const anchorScale = (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : .25;
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 8912b113c..04ac34cc2 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -62,6 +62,10 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps, Sc
@observable private _scriptSuggestedParams: any = "";
@observable private _scriptParamsText: any = "";
+ constructor(props: any) {
+ super(props);
+ }
+
// vars included in fields that store parameters types and names and the script itself
@computed({ keepAlive: true }) get paramsNames() { return this.compileParams.map(p => p.split(":")[0].trim()); }
@computed({ keepAlive: true }) get paramsTypes() { return this.compileParams.map(p => p.split(":")[1].trim()); }
@@ -135,9 +139,6 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps, Sc
@action
onFinish = () => {
this.rootDoc.layoutKey = "layout";
- this.rootDoc._height = 50;
- this.rootDoc._width = 100;
- this.dataDoc.documentText = this.rawScript;
}
// displays error message
@@ -158,8 +159,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps, Sc
params,
typecheck: false
});
- this.dataDoc.documentText = this.rawScript;
- this.dataDoc.data = result.compiled ? new ScriptField(result) : undefined;
+ this.dataDoc[this.fieldKey] = result.compiled ? new ScriptField(result) : undefined;
this.onError(result.compiled ? undefined : result.errors);
return result.compiled;
}
@@ -171,7 +171,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps, Sc
const bindings: { [name: string]: any } = {};
this.paramsNames.forEach(key => bindings[key] = this.dataDoc[key]);
// binds vars so user doesnt have to refer to everything as self.<var>
- ScriptCast(this.dataDoc.data, null)?.script.run({ self: this.rootDoc, this: this.layoutDoc, ...bindings }, this.onError);
+ ScriptCast(this.dataDoc[this.fieldKey], null)?.script.run({ self: this.rootDoc, this: this.layoutDoc, ...bindings }, this.onError);
}
}
@@ -589,7 +589,8 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps, Sc
}
// inputs for scripting div (script box, params box, and params column)
- @computed({ keepAlive: true }) get renderScriptingInputs() {
+ @computed get renderScriptingInputs() {
+ TraceMobx();
// should there be a border? style={{ borderStyle: "groove", borderBlockWidth: "1px" }}
// params box on bottom
@@ -673,6 +674,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps, Sc
// renders script UI if _applied = false and params UI if _applied = true
render() {
+ TraceMobx();
return (
<div className={`scriptingBox`} onContextMenu={this.specificContextMenu}
onPointerUp={!this._function ? this.suggestionPos : undefined}>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index b726a6df9..05355caba 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -41,7 +41,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
@observable private _url: string = "hello";
@observable private _pressX: number = 0;
@observable private _pressY: number = 0;
-
+ private _keyInput = React.createRef<HTMLInputElement>();
private _longPressSecondsHack?: NodeJS.Timeout;
private _outerRef = React.createRef<HTMLDivElement>();
private _iframeRef = React.createRef<HTMLIFrameElement>();
@@ -195,9 +195,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
onValueKeyDown = async (e: React.KeyboardEvent) => {
if (e.key === "Enter") {
- e.stopPropagation();
this.submitURL();
}
+ e.stopPropagation();
}
toggleAnnotationMode = () => {
@@ -237,6 +237,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
onDragOver={this.onUrlDragover}
onChange={this.onURLChange}
onKeyDown={this.onValueKeyDown}
+ onClick={(e) => {
+ this._keyInput.current!.select();
+ e.stopPropagation();
+ }}
+ ref={this._keyInput}
/>
<div style={{
display: "flex",
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 5c084ae92..6ac6c57f4 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -288,6 +288,15 @@ footnote::after {
font-family: inherit;
}
+ blockquote {
+ padding: 10px 10px;
+ font-size: smaller;
+ margin: 0;
+ font-style: italic;
+ background: lightgray;
+ border-left: solid 2px dimgray;
+ }
+
ol, ul {
counter-reset: deci1 0 multi1 0;
padding-left: 1em;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index a5a40a31f..3eecc98a4 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -230,7 +230,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
updateTitle = () => {
if ((this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing
StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.rootDoc.customTitle) {
- const str = this._editorView.state.doc.textContent;
+ let node = this._editorView.state.doc;
+ while (node.firstChild) node = node.firstChild;
+ const str = node.textContent;
const titlestr = str.substr(0, Math.min(40, str.length));
this.dataDoc.title = "-" + titlestr + (str.length > 40 ? "..." : "");
}
@@ -932,7 +934,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
clipboardTextSerializer: this.clipboardTextSerializer,
handlePaste: this.handlePaste,
});
- // applyDevTools.applyDevTools(this._editorView);
+ //applyDevTools.applyDevTools(this._editorView);
const startupText = !rtfField && this._editorView && Field.toString(this.dataDoc[fieldKey] as Field);
if (startupText) {
const { state: { tr }, dispatch } = this._editorView;
@@ -1042,11 +1044,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
if (bounds && this.layoutDoc._chromeStatus !== "disabled") {
const x = Math.min(Math.max(bounds.left, 0), window.innerWidth - RichTextMenu.Instance.width);
let y = Math.min(Math.max(0, bounds.top - RichTextMenu.Instance.height - 50), window.innerHeight - RichTextMenu.Instance.height);
- console.log("y = " + y + " hgt = " + RichTextMenu.Instance.height + " cords = " + coords.top);
if (coords && coords.left > x && coords.left < x + RichTextMenu.Instance.width && coords.top > y && coords.top < y + RichTextMenu.Instance.height + 50) {
y = Math.min(bounds.bottom, window.innerHeight - RichTextMenu.Instance.height);
}
- RichTextMenu.Instance.jumpTo(x, y);
+ setTimeout(() => window.document.activeElement === this.ProseRef?.children[0] && RichTextMenu.Instance.jumpTo(x, y), 250);
}
}
onPointerWheel = (e: React.WheelEvent): void => {
diff --git a/src/client/views/nodes/formattedText/ParagraphNodeSpec.ts b/src/client/views/nodes/formattedText/ParagraphNodeSpec.ts
index d80e64634..30da91710 100644
--- a/src/client/views/nodes/formattedText/ParagraphNodeSpec.ts
+++ b/src/client/views/nodes/formattedText/ParagraphNodeSpec.ts
@@ -28,7 +28,7 @@ const ALIGN_PATTERN = /(left|right|center|justify)/;
// https://github.com/ProseMirror/prosemirror-schema-basic/blob/master/src/schema-basic.js
// :: NodeSpec A plain paragraph textblock. Represented in the DOM
// as a `<p>` element.
-const ParagraphNodeSpec: NodeSpec = {
+export const ParagraphNodeSpec: NodeSpec = {
attrs: {
align: { default: null },
color: { default: null },
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 75cfe6bd1..9c91d8007 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -1,4 +1,5 @@
-import { chainCommands, exitCode, joinDown, joinUp, lift, selectParentNode, setBlockType, splitBlockKeepMarks, toggleMark, wrapIn } from "prosemirror-commands";
+import { chainCommands, exitCode, joinDown, joinUp, lift, selectParentNode, 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";
@@ -177,15 +178,27 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
//command to break line
bind("Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
if (addTextOnRight(false)) return true;
+ const trange = state.selection.$from.blockRange(state.selection.$to);
+ const path = (state.selection.$from as any).path;
+ const depth = trange ? liftTarget(trange) : undefined;
+ const split = path.length > 5 && !path[path.length - 3].textContent && path[path.length - 6].type !== schema.nodes.list_item;
+ if (split && trange && depth !== undefined && depth !== null) {
+ dispatch(state.tr.lift(trange, depth));
+ return true;
+ }
+
const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
- if (!splitListItem(schema.nodes.list_item)(state, dispatch)) {
- 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 cr = state.selection.$from.node().textContent.endsWith("\n");
+ if (cr || !newlineInCode(state, dispatch)) {
+ if (!splitListItem(schema.nodes.list_item)(state, dispatch)) {
+ 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);
+ }
+ })) {
+ return false;
}
- })) {
- return false;
}
}
return true;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 1a961ae21..5e32b3ef8 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -1,30 +1,33 @@
import React = require("react");
-import AntimodeMenu from "../../AntimodeMenu";
-import { observable, action, } from "mobx";
+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 { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, observable } from "mobx";
import { observer } from "mobx-react";
-import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos, Schema } from "prosemirror-model";
-import { schema } from "./schema_rts";
-import { EditorView } from "prosemirror-view";
+import { lift, wrapIn } from "prosemirror-commands";
+import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos } from "prosemirror-model";
+import { wrapInList } from "prosemirror-schema-list";
import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSubscript, faSuperscript, faIndent, faEyeDropper, faCaretDown, faPalette, faHighlighter, faLink, faPaintRoller, faSleigh } from "@fortawesome/free-solid-svg-icons";
-import { updateBullets } from "./ProsemirrorExampleTransfer";
-import { FieldViewProps } from "../FieldView";
+import { EditorView } from "prosemirror-view";
+import { Doc } from "../../../../fields/Doc";
+import { DarkPastelSchemaPalette, PastelSchemaPalette } from '../../../../fields/SchemaHeaderField';
import { Cast, StrCast } from "../../../../fields/Types";
-import { FormattedTextBoxProps, FormattedTextBox } from "./FormattedTextBox";
import { unimplementedFunction, Utils } from "../../../../Utils";
-import { wrapInList } from "prosemirror-schema-list";
-import { PastelSchemaPalette, DarkPastelSchemaPalette } from '../../../../fields/SchemaHeaderField';
-import "./RichTextMenu.scss";
import { DocServer } from "../../../DocServer";
-import { Doc } from "../../../../fields/Doc";
-import { SelectionManager } from "../../../util/SelectionManager";
import { LinkManager } from "../../../util/LinkManager";
-const { toggleMark, setBlockType } = require("prosemirror-commands");
+import { SelectionManager } from "../../../util/SelectionManager";
+import AntimodeMenu from "../../AntimodeMenu";
+import { FieldViewProps } from "../FieldView";
+import { FormattedTextBox, FormattedTextBoxProps } from "./FormattedTextBox";
+import { updateBullets } from "./ProsemirrorExampleTransfer";
+import "./RichTextMenu.scss";
+import { schema } from "./schema_rts";
+import { TraceMobx } from "../../../../fields/util";
+const { toggleMark } = require("prosemirror-commands");
library.add(faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, faCaretDown, faPalette, faHighlighter, faLink, faPaintRoller);
+
@observer
export default class RichTextMenu extends AntimodeMenu {
static Instance: RichTextMenu;
@@ -69,6 +72,7 @@ export default class RichTextMenu extends AntimodeMenu {
super(props);
RichTextMenu.Instance = this;
this._canFade = false;
+ this.Pinned = true;
this.fontSizeOptions = [
{ mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize },
@@ -161,11 +165,10 @@ export default class RichTextMenu extends AntimodeMenu {
return;
}
this.view = view;
- const state = view.state;
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;
+ if (lastState?.doc.eq(view.state.doc) && lastState.selection.eq(view.state.selection)) return;
// update active marks
const activeMarks = this.getActiveMarksOnSelection();
@@ -173,18 +176,18 @@ export default class RichTextMenu extends AntimodeMenu {
// update active font family and size
const active = this.getActiveFontStylesOnSelection();
- const activeFamilies = active && active.get("families");
- const activeSizes = active && active.get("sizes");
+ const activeFamilies = active?.get("families");
+ const activeSizes = active?.get("sizes");
- this.activeFontFamily = !activeFamilies || activeFamilies.length === 0 ? "Arial" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various";
- this.activeFontSize = !activeSizes || activeSizes.length === 0 ? "13pt" : activeSizes.length === 1 ? String(activeSizes[0]) + "pt" : "various";
+ this.activeFontFamily = !activeFamilies?.length ? "Arial" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various";
+ this.activeFontSize = !activeSizes?.length ? "13pt" : activeSizes.length === 1 ? String(activeSizes[0]) : "various";
// update link in current selection
const targetTitle = await this.getTextLinkTargetTitle();
this.setCurrentLink(targetTitle);
}
- setMark = (mark: Mark, state: EditorState<any>, dispatch: any) => {
+ setMark = (mark: Mark, state: EditorState<any>, dispatch: any, dontToggle: boolean = false) => {
if (mark) {
const node = (state.selection as NodeSelection).node;
if (node?.type === schema.nodes.ordered_list) {
@@ -194,14 +197,15 @@ export default class RichTextMenu extends AntimodeMenu {
if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, fontColor: 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 {
+ } else if (dontToggle) {
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);
+ if (!tx.doc.rangeHasMark(from, to, mark.type)) { // hack -- should have just set the mark in the first place
+ toggleMark(mark.type, mark.attrs)({ tr: tx, doc: tx.doc, selection: tx.selection, storedMarks: tx.storedMarks }, dispatch);
+ } else dispatch(tx);
});
+ } else {
+ toggleMark(mark.type, mark.attrs)(state, dispatch);
}
}
}
@@ -368,11 +372,11 @@ export default class RichTextMenu extends AntimodeMenu {
}
changeFontSize = (mark: Mark, view: EditorView) => {
- this.setMark(view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize }), view.state, view.dispatch);
+ this.setMark(view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize }), view.state, view.dispatch, true);
}
changeFontFamily = (mark: Mark, view: EditorView) => {
- this.setMark(view.state.schema.marks.pFontFamily.create({ family: mark.attrs.family }), view.state, view.dispatch);
+ this.setMark(view.state.schema.marks.pFontFamily.create({ family: mark.attrs.family }), view.state, view.dispatch, true);
}
// TODO: remove doesn't work
@@ -406,7 +410,17 @@ export default class RichTextMenu extends AntimodeMenu {
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));
+ dispatch?.(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
+ return true;
+ }
+
+ insertBlockquote(state: EditorState<any>, dispatch: any) {
+ const path = (state.selection.$from as any).path;
+ if (path.length > 6 && path[path.length - 6].type === schema.nodes.blockquote) {
+ lift(state, dispatch);
+ } else {
+ wrapIn(schema.nodes.blockquote)(state, dispatch);
+ }
return true;
}
@@ -539,7 +553,7 @@ export default class RichTextMenu extends AntimodeMenu {
dispatch(state.tr.addStoredMark(colorMark));
return false;
}
- this.setMark(colorMark, state, dispatch);
+ this.setMark(colorMark, state, dispatch, true);
}
@action toggleHighlightDropdown() { this.showHighlightDropdown = !this.showHighlightDropdown; }
@@ -752,7 +766,7 @@ export default class RichTextMenu extends AntimodeMenu {
}
render() {
-
+ 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)),
@@ -766,6 +780,7 @@ export default class RichTextMenu extends AntimodeMenu {
this.createLinkButton(),
this.createBrushButton(),
this.createButton("indent", "Summarize", undefined, this.insertSummarizer),
+ this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote),
]}</div>;
const row2 = <div className="antimodeMenu-row row-2" key="antimodemenu row2">
@@ -776,11 +791,11 @@ export default class RichTextMenu extends AntimodeMenu {
this.createNodesDropdown(this.activeListType, this.listTypeOptions, "nodes")]}
</div>
<div key="button">
- <div key="collapser">
+ {/* <div key="collapser">
<button className="antimodeMenu-button" key="collapse menu" title="Collapse menu" onClick={this.toggleCollapse} style={{ backgroundColor: this.collapsed ? "#121212" : "", width: 25 }}>
<FontAwesomeIcon icon="chevron-left" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.3s", transform: `rotate(${this.collapsed ? 180 : 0}deg)` }} />
</button>
- </div>
+ </div> */}
<button className="antimodeMenu-button" key="pin menu" title="Pin menu" onClick={this.toggleMenuPin} style={{ backgroundColor: this.Pinned ? "#121212" : "", display: this.collapsed ? "none" : undefined }}>
<FontAwesomeIcon icon="thumbtack" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.Pinned ? 45 : 0}deg)` }} />
</button>
@@ -788,7 +803,7 @@ export default class RichTextMenu extends AntimodeMenu {
</div>;
return (
- <div className="richTextMenu" onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+ <div className="richTextMenu" onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} >
{this.getElementWithRows([row1, row2], 2, false)}
</div>
);
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 33ef67ff5..0a867912f 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -1,7 +1,7 @@
import React = require("react");
import { DOMOutputSpecArray, Fragment, MarkSpec, Node, NodeSpec, Schema, Slice } from "prosemirror-model";
import { bulletList, listItem, orderedList } from 'prosemirror-schema-list';
-import ParagraphNodeSpec from "./ParagraphNodeSpec";
+import { ParagraphNodeSpec, toParagraphDOM, getParagraphNodeAttrs } from "./ParagraphNodeSpec";
const blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
@@ -32,13 +32,29 @@ export const nodes: { [index: string]: NodeSpec } = {
// :: NodeSpec A blockquote (`<blockquote>`) wrapping one or more blocks.
blockquote: {
- content: "block+",
+ content: "block*",
group: "block",
defining: true,
parseDOM: [{ tag: "blockquote" }],
toDOM() { return blockquoteDOM; }
},
+
+ // blockquote: {
+ // ...ParagraphNodeSpec,
+ // defining: true,
+ // parseDOM: [{
+ // tag: "blockquote", getAttrs(dom: any) {
+ // return getParagraphNodeAttrs(dom);
+ // }
+ // }],
+ // toDOM(node: any) {
+ // const dom = toParagraphDOM(node);
+ // (dom as any)[0] = 'blockquote';
+ // return dom;
+ // },
+ // },
+
// :: NodeSpec A horizontal rule (`<hr>`).
horizontal_rule: {
group: "block",
@@ -67,8 +83,8 @@ export const nodes: { [index: string]: NodeSpec } = {
// nodes by default. Represented as a `<pre>` element with a
// `<code>` element inside of it.
code_block: {
- content: "text*",
- marks: "",
+ content: "inline*",
+ marks: "_",
group: "block",
code: true,
defining: true,
diff --git a/src/client/views/nodes/formattedText/prosemirrorPatches.js b/src/client/views/nodes/formattedText/prosemirrorPatches.js
index 269423482..763961958 100644
--- a/src/client/views/nodes/formattedText/prosemirrorPatches.js
+++ b/src/client/views/nodes/formattedText/prosemirrorPatches.js
@@ -136,4 +136,6 @@ function wrappingInputRule(regexp, nodeType, getAttrs, joinPredicate, customWith
(!joinPredicate || joinPredicate(match, before))) { tr.join(start - 1); }
return tr
})
-} \ No newline at end of file
+}
+
+