aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/formattedText
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/formattedText')
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx3
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss30
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx221
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.scss8
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx22
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx55
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts12
-rw-r--r--src/client/views/nodes/formattedText/RichTextSchema.tsx3
8 files changed, 215 insertions, 139 deletions
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index ffa6e904a..7cd92b8b9 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -82,7 +82,7 @@ export class DashDocView extends React.Component<IDashDocView> {
const { scale, translateX, translateY } = Utils.GetScreenTransform(outerElement);
return new Transform(-translateX, -translateY, 1).scale(1 / this.contentScaling() / scale);
}
- contentScaling = () => NumCast(this._dashDoc!._nativeWidth) > 0 ? this._dashDoc![WidthSym]() / NumCast(this._dashDoc!._nativeWidth) : 1;
+ contentScaling = () => Doc.NativeWidth(this._dashDoc) > 0 ? this._dashDoc![WidthSym]() / Doc.NativeWidth(this._dashDoc) : 1;
outerFocus = (target: Doc) => this._textBox.props.focus(this._textBox.props.Document); // ideally, this would scroll to show the focus target
@@ -246,6 +246,7 @@ export class DashDocView extends React.Component<IDashDocView> {
bringToFront={emptyFunction}
dontRegisterView={false}
docFilters={this.props.tbox?.props.docFilters || returnEmptyFilter}
+ docRangeFilters={this.props.tbox?.props.docRangeFilters || returnEmptyFilter}
searchFilterDocs={this.props.tbox?.props.searchFilterDocs || returnEmptyDoclist}
ContainingCollectionView={this._textBox.props.ContainingCollectionView}
ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index d1109b388..b75cc230f 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -12,7 +12,6 @@
.formattedTextBox-cont {
touch-action: none;
- cursor: text;
background: inherit;
padding: 0;
border-width: 0px;
@@ -23,8 +22,7 @@
border-style: solid;
overflow-y: auto;
overflow-x: hidden;
- color: initial;
- max-height: 100%;
+ color: inherit;
display: flex;
flex-direction: row;
transition: opacity 1s;
@@ -38,6 +36,7 @@
}
}
+.formattedTextBox-outer-selected,
.formattedTextBox-outer {
position: relative;
overflow: auto;
@@ -45,6 +44,9 @@
width: 100%;
height: unset;
}
+.formattedTextBox-outer-selected {
+ cursor: text;
+}
.formattedTextBox-sidebar-handle {
position: absolute;
@@ -106,6 +108,15 @@
border-width: 1px;
}
}
+.formattedTextBox-inner-rounded-selected,
+.formattedTextBox-inner-selected {
+ .ProseMirror {
+ padding:10px;
+ }
+ .ProseMirror:hover {
+ background: unset;
+ }
+}
// .menuicon {
// display: inline-block;
@@ -332,15 +343,6 @@ footnote::after {
.multi4:before { transition: 0.5s;counter-increment: multi4; display: inline-block; vertical-align: top; margin-left: -4.2em; width: 4.2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) "."counter(multi4, lower-roman) ". "; }
}
-.formattedTextBox-inner-rounded-selected,
-.formattedTextBox-inner-selected {
- .ProseMirror {
- padding:10px;
- }
- .ProseMirror:hover {
- background: unset;
- }
-}
@media only screen and (max-width: 1000px) {
@import "../../globalCssVariables";
@@ -357,7 +359,6 @@ footnote::after {
.formattedTextBox-cont {
touch-action: none;
- cursor: text;
background: inherit;
padding: 0;
border-width: 0px;
@@ -383,6 +384,7 @@ footnote::after {
}
}
+ .formattedTextBox-outer-selected,
.formattedTextBox-outer {
position: relative;
overflow: auto;
@@ -399,7 +401,7 @@ footnote::after {
height: 35px;
background: lightgray;
border-radius: 20px;
- cursor:grabbing;
+ cursor: grabbing;
}
.formattedTextBox-sidebar,
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 1e6477f43..fe38939c5 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1,5 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { isEqual } from "lodash";
+import { isEqual, max } from "lodash";
import { action, computed, IReactionDisposer, Lambda, observable, reaction, runInAction, trace } from "mobx";
import { observer } from "mobx-react";
import { baseKeymap, selectAll } from "prosemirror-commands";
@@ -11,7 +11,7 @@ import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "
import { ReplaceStep } from 'prosemirror-transform';
import { EditorView } from "prosemirror-view";
import { DateField } from '../../../../fields/DateField';
-import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, AclEdit, AclAdmin } from "../../../../fields/Doc";
+import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, AclEdit, AclAdmin, UpdatingFromServer, ForceServerWrite } from "../../../../fields/Doc";
import { documentSchema } from '../../../../fields/documentSchemas';
import applyDevTools = require("prosemirror-dev-tools");
import { removeMarkWithAttrs } from "./prosemirrorPatches";
@@ -22,8 +22,8 @@ import { RichTextField } from "../../../../fields/RichTextField";
import { RichTextUtils } from '../../../../fields/RichTextUtils';
import { makeInterface } from "../../../../fields/Schema";
import { Cast, DateCast, NumCast, StrCast, ScriptCast, BoolCast } from "../../../../fields/Types";
-import { TraceMobx, OVERRIDE_acl, GetEffectiveAcl } from '../../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents, OmitKeys } from '../../../../Utils';
+import { TraceMobx, GetEffectiveAcl } from '../../../../fields/util';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents, OmitKeys, smoothScroll } from '../../../../Utils';
import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
import { DocServer } from "../../../DocServer";
import { Docs, DocUtils } from '../../../documents/Documents';
@@ -58,6 +58,9 @@ import "./FormattedTextBox.scss";
import { FormattedTextBoxComment, formattedTextBoxCommentPlugin, findLinkMark } from './FormattedTextBoxComment';
import React = require("react");
import { DocumentManager } from '../../../util/DocumentManager';
+import { CollectionStackingView } from '../../collections/CollectionStackingView';
+import { CollectionViewType } from '../../collections/CollectionView';
+import { SnappingManager } from '../../../util/SnappingManager';
export interface FormattedTextBoxProps {
makeLink?: () => Opt<Doc>; // bcz: hack: notifies the text document when the container has made a link. allows the text doc to react and setup a hyeprlink for any selected text
@@ -286,10 +289,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
}
} else {
-
- const json = JSON.parse(Cast(this.dataDoc[this.fieldKey], RichTextField)?.Data!);
- json.selection = state.toJSON().selection;
- this._editorView.updateState(EditorState.fromJSON(this.config, json));
+ const jsonstring = Cast(this.dataDoc[this.fieldKey], RichTextField)?.Data!;
+ if (jsonstring) {
+ const json = JSON.parse(jsonstring);
+ json.selection = state.toJSON().selection;
+ this._editorView.updateState(EditorState.fromJSON(this.config, json));
+ }
}
}
}
@@ -557,11 +562,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
sidebarDown = (e: React.PointerEvent) => {
setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction,
- () => (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, "0%") === "0%" ? "25%" : "0%"));
+ () => setTimeout(action(() => {
+ const prevWidth = this.sidebarWidth();
+ this.layoutDoc._showSidebar = ((this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, "0%") === "0%" ? "50%" : "0%")) !== "0%";
+ this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
+ })), false);
}
sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
const bounds = this.CurrentDiv.getBoundingClientRect();
this.layoutDoc._sidebarWidthPercent = "" + 100 * Math.max(0, (1 - (e.clientX - bounds.left) / bounds.width)) + "%";
+ this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== "0%";
return false;
}
@undoBatch
@@ -606,7 +616,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const uicontrols: ContextMenuProps[] = [];
- uicontrols.push({ description: `${this.layoutDoc._showSidebar ? "Hide" : "Show"} Sidebar`, event: () => this.layoutDoc._showSidebar = !this.layoutDoc._showSidebar, icon: "expand-arrows-alt" });
uicontrols.push({ description: `${this.layoutDoc._showAudio ? "Hide" : "Show"} Dictation Icon`, event: () => this.layoutDoc._showAudio = !this.layoutDoc._showAudio, icon: "expand-arrows-alt" });
uicontrols.push({ description: "Show Highlights...", noexpand: true, subitems: highlighting, icon: "hand-point-right" });
!Doc.UserDoc().noviceMode && uicontrols.push({ description: `Create TimeStamp When ${this.layoutDoc._timeStampOnEnter ? "Pause" : "Enter"}`, event: () => this.layoutDoc._timeStampOnEnter = !this.layoutDoc._timeStampOnEnter, icon: "expand-arrows-alt" });
@@ -654,7 +663,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const optionItems = options && "subitems" in options ? options.subitems : [];
!Doc.UserDoc().noviceMode && optionItems.push({ description: !this.Document._singleLine ? "Make Single Line" : "Make Multi Line", event: () => this.layoutDoc._singleLine = !this.layoutDoc._singleLine, icon: "expand-arrows-alt" });
optionItems.push({ description: `${this.Document._autoHeight ? "Lock" : "Auto"} Height`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
- optionItems.push({ description: `${!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Lock" : "Unlock"} Aspect`, event: this.toggleNativeDimensions, icon: "snowflake" });
+ optionItems.push({ description: `${!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? "Lock" : "Unlock"} Aspect`, event: this.toggleNativeDimensions, icon: "snowflake" });
!options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
this._downX = this._downY = Number.NaN;
}
@@ -796,13 +805,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
tr = tr.addMark(pos, pos + node.nodeSize, link);
}
});
- OVERRIDE_acl(true);
+ this.dataDoc[ForceServerWrite] = this.dataDoc[UpdatingFromServer] = true; // need to allow permissions for adding links to readonly/augment only documents
this._editorView!.dispatch(tr.removeMark(sel.from, sel.to, splitter));
- OVERRIDE_acl(false);
+ this.dataDoc[UpdatingFromServer] = this.dataDoc[ForceServerWrite] = false;
}
}
componentDidMount() {
this._cachedLinks = DocListCast(this.Document.links);
+ this._disposers.sidebarheight = reaction(
+ () => ({ annoHeight: NumCast(this.rootDoc[this.annotationKey + "-height"]), textHeight: NumCast(this.rootDoc[this.fieldKey + "-height"]) }),
+ ({ annoHeight, textHeight }) => {
+ this.layoutDoc._autoHeight && (this.rootDoc._height = Math.max(annoHeight, textHeight));
+ });
this._disposers.links = reaction(() => DocListCast(this.Document.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks
newLinks => {
this._cachedLinks.forEach(l => !newLinks.includes(l) && this.RemoveLinkFromDoc(l));
@@ -821,8 +835,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
() => this.props.makeLink?.(),
(linkDoc: Opt<Doc>) => {
if (linkDoc) {
- const anchor2Title = linkDoc.anchor2 instanceof Doc ? StrCast(linkDoc.anchor2.title) : "-untitled-";
- const anchor2Id = linkDoc.anchor2 instanceof Doc ? linkDoc.anchor2[Id] : "";
+ const a1 = Cast(linkDoc.anchor1, Doc, null);
+ const a2 = Cast(linkDoc.anchor2, Doc, null);
+ const otherAnchor = Doc.AreProtosEqual(a1, this.rootDoc) ? a2 : a1;
+ const anchor2Title = StrCast(otherAnchor.title, "-untitled-");
+ const anchor2Id = otherAnchor?.[Id] || "";
this.makeLinkToSelection(linkDoc[Id], anchor2Title, "add:right", anchor2Id);
}
},
@@ -873,7 +890,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
({ width, autoHeight }) => width !== undefined && setTimeout(() => this.tryUpdateHeight(), 0)
);
this._disposers.height = reaction(
- () => NumCast(this.layoutDoc._height),
+ () => Cast(this.layoutDoc._height, "number", null),
action(height => {
if (height !== undefined && height <= 20 && height < NumCast(this.layoutDoc._delayAutoHeight, 20)) {
this.layoutDoc._delayAutoHeight = height;
@@ -906,27 +923,28 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
async (scrollToLinkID) => {
const findLinkFrag = (frag: Fragment, editor: EditorView) => {
const nodes: Node[] = [];
- let offset = 0;
+ let hadStart = start !== 0;
frag.forEach((node, index) => {
const examinedNode = findLinkNode(node, editor);
- if (examinedNode?.textContent) {
- nodes.push(examinedNode);
- offset = index;
+ if (examinedNode?.node.textContent) {
+ nodes.push(examinedNode.node);
+ !hadStart && (start = index + examinedNode.start);
+ hadStart = true;
}
});
- return { frag: Fragment.fromArray(nodes), start: start + offset };
+ return { frag: Fragment.fromArray(nodes), start };
};
const findLinkNode = (node: Node, editor: EditorView) => {
if (!node.isText) {
const content = findLinkFrag(node.content, editor);
- return node.copy(content.frag);
+ return { node: node.copy(content.frag), start: content.start };
}
const marks = [...node.marks];
const linkIndex = marks.findIndex(mark => mark.type === editor.state.schema.marks.linkAnchor);
- return linkIndex !== -1 && marks[linkIndex].attrs.allLinks.find((item: { href: string }) => scrollToLinkID === item.href.replace(/.*\/doc\//, "")) ? node : undefined;
+ return linkIndex !== -1 && marks[linkIndex].attrs.allLinks.find((item: { href: string }) => scrollToLinkID === item.href.replace(/.*\/doc\//, "")) ? { node, start: 0 } : undefined;
};
- const start = 0;
+ let start = 0;
if (this._editorView && scrollToLinkID) {
const editor = this._editorView;
const ret = findLinkFrag(editor.state.doc.content, editor);
@@ -948,7 +966,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
{ fireImmediately: true }
);
this._disposers.scroll = reaction(() => NumCast(this.layoutDoc._scrollTop),
- pos => this._scrollRef.current && this._scrollRef.current.scrollTo({ top: pos }), { fireImmediately: true }
+ pos => this._scrollRef.current?.scrollTo({ top: pos }), { fireImmediately: true }
+ );
+ this._disposers.scrollY = reaction(() => Cast(this.layoutDoc._scrollY, "number", null),
+ scrollY => {
+ if (scrollY !== undefined) {
+ const delay = this._scrollRef.current ? 0 : 250; // wait for mainCont and try again to scroll
+ const durationStr = StrCast(this.Document._viewTransition).match(/([0-9]*)ms/);
+ const duration = durationStr ? Number(durationStr[1]) : 1000;
+ setTimeout(() => this._scrollRef.current && smoothScroll(duration, this._scrollRef.current, Math.abs(scrollY || 0)), delay);
+ setTimeout(() => { this.Document._scrollTop = scrollY; this.Document._scrollY = undefined; }, duration + delay);
+ }
+ }, { fireImmediately: true }
);
setTimeout(() => this.tryUpdateHeight(NumCast(this.layoutDoc.limitHeight)));
@@ -1114,6 +1143,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
}
+ private isActiveTab(el: Element | null | undefined) {
+ while (el && el !== document.body) {
+ if (getComputedStyle(el).display === "none") return false;
+ el = el.parentNode as any;
+ }
+ return true;
+ }
+
private setupEditor(config: any, fieldKey: string) {
const curText = Cast(this.dataDoc[this.props.fieldKey], RichTextField, null);
const rtfField = Cast((!curText?.Text && this.layoutDoc[this.props.fieldKey]) || this.dataDoc[fieldKey], RichTextField);
@@ -1152,7 +1189,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
const selectOnLoad = this.rootDoc[Id] === FormattedTextBox.SelectOnLoad;
- if (selectOnLoad && !this.props.dontRegisterView && !this.props.dontSelectOnLoad) {
+ if (selectOnLoad && !this.props.dontRegisterView && !this.props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
FormattedTextBox.SelectOnLoad = "";
this.props.select(false);
if (FormattedTextBox.SelectOnLoadChar && this._editorView) {
@@ -1160,7 +1197,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) });
const curMarks = this._editorView.state.storedMarks ?? $from?.marksAcross(this._editorView.state.selection.$head) ?? [];
const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
- this._editorView.dispatch(this._editorView.state.tr.setStoredMarks(storedMarks).insertText(FormattedTextBox.SelectOnLoadChar).setStoredMarks(storedMarks));
+ const tr = this._editorView.state.tr.setStoredMarks(storedMarks).insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size).setStoredMarks(storedMarks);
+ this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
FormattedTextBox.SelectOnLoadChar = "";
} else if (curText?.Text) {
selectAll(this._editorView!.state, this._editorView?.dispatch);
@@ -1301,6 +1339,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
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);
}
+ this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props);
setTimeout(() => window.document.activeElement === this.ProseRef?.children[0] && RichTextMenu.Instance.jumpTo(x, y), 250);
}
}
@@ -1408,7 +1447,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const self = this;
return new Plugin({
view(newView) {
- self.props.isSelected(true) && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView);
+ runInAction(() => self.props.isSelected(true) && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView));
return self.menuPlugin = new RichTextMenuPlugin({ editorProps: this.props });
}
});
@@ -1420,23 +1459,20 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
public endUndoTypingBatch() {
const wasUndoing = this._undoTyping;
- if (this._undoTyping) {
- this._undoTyping.end();
- this._undoTyping = undefined;
- }
+ this._undoTyping?.end();
+ this._undoTyping = undefined;
return wasUndoing;
}
public static LiveTextUndo: UndoManager.Batch | undefined;
public static HadSelection: boolean = false;
onBlur = (e: any) => {
+ RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined);
FormattedTextBox.HadSelection = window.getSelection()?.toString() !== "";
this.endUndoTypingBatch();
this.doLinkOnDeselect();
FormattedTextBox.LiveTextUndo?.end();
FormattedTextBox.LiveTextUndo = undefined;
- // move the richtextmenu offscreen
- //if (!RichTextMenu.Instance.Pinned) RichTextMenu.Instance.delayHide();
}
_lastTimedMark: Mark | undefined = undefined;
@@ -1509,38 +1545,92 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}, 10);
} else {
try {
- const boxHeight = Number(getComputedStyle(this._boxRef.current!).height.replace("px", ""));
+ const boxHeight = Number(getComputedStyle(this._boxRef.current!).height.replace("px", "")) * NumCast(this.Document._viewScale, 1);
const outer = this.rootDoc[HeightSym]() - boxHeight - (this.props.ChromeHeight ? this.props.ChromeHeight() : 0);
- this.rootDoc._height = newHeight + Math.max(0, outer);
- this.layoutDoc._nativeHeight = nh ? scrollHeight : undefined;
+ this.rootDoc[this.fieldKey + "-height"] = newHeight + Math.max(0, outer);
} catch (e) { console.log("Error in tryUpdateHeight"); }
}
- }
+ } //else this.rootDoc[this.fieldKey + "-height"] = 0;
+ }
+
+ @computed get audioHandle() {
+ return !this.layoutDoc._showAudio ? (null) :
+ <div className="formattedTextBox-dictation" onClick={action(e => this._recording = !this._recording)} >
+ <FontAwesomeIcon className="formattedTextBox-audioFont" style={{ color: this._recording ? "red" : "blue", transitionDelay: "0.6s", opacity: this._recording ? 1 : 0.25, }} icon={"microphone"} size="sm" />
+ </div>;
+ }
+
+ @computed get sidebarHandle() {
+ const annotated = DocListCast(this.dataDoc[this.annotationKey]).filter(d => d?.author).length;
+ return !this.props.isSelected() && !(annotated && !this.sidebarWidth()) ? (null) :
+ <div className="formattedTextBox-sidebar-handle"
+ style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${this.sidebarWidth() ? "- 5px" : "- 10px"}))`, background: annotated ? "lightBlue" : undefined }}
+ onPointerDown={this.sidebarDown}
+ />;
+ }
+
+ sidebarContentScaling = () => this.props.ContentScaling() * NumCast(this.layoutDoc._viewScale, 1);
+ @computed get sidebarCollection() {
+ const fitToBox = this.props.Document._fitToBox;
+ const collectionProps = {
+ ...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit,
+ NativeWidth: returnZero,
+ NativeHeight: returnZero,
+ PanelHeight: this.props.PanelHeight,
+ PanelWidth: this.sidebarWidth,
+ xMargin: 0,
+ yMargin: 0,
+ chromeStatus: "enabled",
+ scaleField: this.annotationKey + "-scale",
+ annotationsKey: this.annotationKey,
+ isAnnotationOverlay: true,
+ fitToBox: fitToBox,
+ focus: this.props.focus,
+ isSelected: this.props.isSelected,
+ select: emptyFunction,
+ active: this.annotationsActive,
+ ContentScaling: this.sidebarContentScaling,
+ whenActiveChanged: this.whenActiveChanged,
+ removeDocument: this.removeDocument,
+ moveDocument: this.moveDocument,
+ addDocument: this.addDocument,
+ CollectionView: undefined,
+ ScreenToLocalTransform: this.sidebarScreenToLocal,
+ renderDepth: this.props.renderDepth + 1,
+ ContainingCollectionDoc: this.props.ContainingCollectionDoc,
+ };
+ return !this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) :
+ <div className={"formattedTextBox-sidebar" + (Doc.GetSelectedTool() !== InkTool.None ? "-inking" : "")}
+ style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+ {this.layoutDoc.sidebarViewType === CollectionViewType.Freeform ? <CollectionFreeFormView {...collectionProps} /> : <CollectionStackingView {...collectionProps} />}
+ </div>;
}
@computed get sidebarWidthPercent() { return StrCast(this.layoutDoc._sidebarWidthPercent, "0%"); }
sidebarWidth = () => Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth();
- sidebarScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth()) / this.props.ContentScaling(), 0);
- @computed get sidebarColor() { return StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "transparent")); }
+ sidebarScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth()) / this.props.ContentScaling(), 0).scale(1 / NumCast(this.layoutDoc._viewScale, 1));
+ @computed get sidebarColor() { return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "#e4e4e4")); }
render() {
TraceMobx();
const selected = this.props.isSelected();
const active = this.active();
const scale = this.props.hideOnLeave ? 1 : this.props.ContentScaling() * NumCast(this.layoutDoc._viewScale, 1);
const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
- const interactive = Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc._isBackground;
- selected && setTimeout(() => this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props)); // need to make sure that we update a text box that is selected after updating the one that was deselected
- if (!selected && FormattedTextBoxComment.textBox === this) { FormattedTextBoxComment.Hide(); }
+ const interactive = (Doc.GetSelectedTool() === InkTool.None || SnappingManager.GetIsDragging()) && !this.layoutDoc._isBackground;
+ if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(() => FormattedTextBoxComment.Hide());
const minimal = this.props.ignoreAutoHeight;
- const selPad = (selected && !this.layoutDoc._singleLine) || minimal ? -10 : 0;
- const selclass = selected && !this.layoutDoc._singleLine ? "-selected" : "";
+ const margins = NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0);
+ const selPad = Math.min(margins, 10);
+ const padding = Math.max(margins + ((selected && !this.layoutDoc._singleLine) || minimal ? -selPad : 0), 0);
+ const selclass = selected && !this.layoutDoc._singleLine && margins >= 10 ? "-selected" : "";
return (
- <div className={"formattedTextBox-cont"} ref={this._boxRef}
+ <div className="formattedTextBox-cont" ref={this._boxRef}
style={{
transform: `scale(${scale})`,
transformOrigin: "top left",
width: `${100 / scale}%`,
height: `calc(${100 / scale}% - ${this.props.ChromeHeight?.() || 0}px)`,
+ overflowY: this.layoutDoc._autoHeight ? "hidden" : undefined,
...this.styleFromLayoutString(scale) // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._headerHeight}px' >
}}>
<div className={`formattedTextBox-cont`} ref={this._ref}
@@ -1575,45 +1665,20 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
})}
onDoubleClick={this.onDoubleClick}
>
- <div className={`formattedTextBox-outer`} ref={this._scrollRef}
- style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: !active ? "none" : undefined }}
+ <div className={`formattedTextBox-outer${selclass}`} ref={this._scrollRef}
+ style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: !active && !SnappingManager.GetIsDragging() ? "none" : undefined }}
onScroll={this.onscrolled} onDrop={this.ondrop} >
<div className={minimal ? "formattedTextBox-minimal" : `formattedTextBox-inner${rounded}${selclass}`} ref={this.createDropTarget}
style={{
overflow: this.layoutDoc._singleLine ? "hidden" : undefined,
- 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: !active ? ((this.layoutDoc.isLinkButton || this.props.onClick) ? "none" : undefined) : undefined
+ padding: this.layoutDoc._textBoxPadding ? StrCast(this.layoutDoc._textBoxPadding) : `${padding}px`,
+ pointerEvents: !active && !SnappingManager.GetIsDragging() ? ((this.layoutDoc.isLinkButton || this.props.onClick) ? "none" : undefined) : undefined
}}
/>
</div>
- {!this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) :
- <div className={"formattedTextBox-sidebar" + (Doc.GetSelectedTool() !== InkTool.None ? "-inking" : "")} style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
- <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
- PanelHeight={active ? () => 1000 : this.props.PanelHeight}
- PanelWidth={this.sidebarWidth}
- scaleField={this.annotationKey + "-scale"}
- annotationsKey={this.annotationKey}
- isAnnotationOverlay={true}
- focus={this.props.focus}
- isSelected={this.props.isSelected}
- select={emptyFunction}
- active={this.annotationsActive}
- ContentScaling={returnOne}
- whenActiveChanged={this.whenActiveChanged}
- removeDocument={this.removeDocument}
- moveDocument={this.moveDocument}
- addDocument={this.addDocument}
- CollectionView={undefined}
- ScreenToLocalTransform={this.sidebarScreenToLocal}
- renderDepth={this.props.renderDepth + 1}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc} />
- </div>}
- {selected ? <div className="formattedTextBox-sidebar-handle" style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} - 5px))` }} onPointerDown={this.sidebarDown} /> : (null)}
- {!this.layoutDoc._showAudio ? (null) :
- <div className="formattedTextBox-dictation" onClick={action(e => this._recording = !this._recording)} >
- <FontAwesomeIcon className="formattedTextBox-audioFont" style={{ color: this._recording ? "red" : "blue", transitionDelay: "0.6s", opacity: this._recording ? 1 : 0.25, }} icon={"microphone"} size="sm" />
- </div>}
+ {this.sidebarCollection}
+ {this.sidebarHandle}
+ {this.audioHandle}
</div>
</div>
);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss b/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss
index 6a403cb17..81afba4d7 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss
@@ -13,11 +13,12 @@
.FormattedTextBoxComment {
background-color: white;
border: 8px solid white;
+ //width: 200px;
//display: flex;
.FormattedTextBoxComment-info {
- margin-bottom: 9px;
+ margin-bottom: 37px;
.FormattedTextBoxComment-title {
padding-right: 4px;
@@ -28,9 +29,6 @@
font-style: italic;
color: rgb(95, 97, 102);
font-size: 10px;
- padding-bottom: 4px;
- margin-bottom: 5px;
-
}
}
@@ -61,7 +59,7 @@
}
.FormattedTextBoxComment-preview-wrapper {
- width: 170px;
+ //width: 170px;
height: 170px;
overflow: hidden;
//padding-top: 5px;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 14bbee1ac..3e5a40084 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -6,7 +6,7 @@ import { EditorState, Plugin } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import * as ReactDOM from 'react-dom';
import wiki from "wikijs";
-import { Doc, DocCastAsync, Opt } from "../../../../fields/Doc";
+import { Doc, DocCastAsync, Opt, DocListCast } from "../../../../fields/Doc";
import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, Utils } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
@@ -15,9 +15,9 @@ import { DocumentType } from "../../../documents/DocumentTypes";
import { LinkManager } from "../../../util/LinkManager";
import { Transform } from "../../../util/Transform";
import { undoBatch } from "../../../util/UndoManager";
-import { LinkMenuItem } from "../../linking/LinkMenuItem";
import { ContentFittingDocumentView } from "../ContentFittingDocumentView";
import { DocumentLinksButton } from "../DocumentLinksButton";
+import { DocumentView } from "../DocumentView";
import { LinkDocPreview } from "../LinkDocPreview";
import { FormattedTextBox } from "./FormattedTextBox";
import './FormattedTextBoxComment.scss';
@@ -88,7 +88,7 @@ export class FormattedTextBoxComment {
FormattedTextBoxComment.tooltipInput.type = "checkbox";
FormattedTextBoxComment.tooltip = document.createElement("div");
FormattedTextBoxComment.tooltipText = document.createElement("div");
- FormattedTextBoxComment.tooltipText.style.width = "100%";
+ //FormattedTextBoxComment.tooltipText.style.width = "100%";
FormattedTextBoxComment.tooltipText.style.height = "100%";
FormattedTextBoxComment.tooltipText.style.textOverflow = "ellipsis";
FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText);
@@ -96,7 +96,7 @@ export class FormattedTextBoxComment {
FormattedTextBoxComment.tooltip.style.pointerEvents = "all";
FormattedTextBoxComment.tooltip.style.maxWidth = "400px";
FormattedTextBoxComment.tooltip.style.maxHeight = "235px";
- FormattedTextBoxComment.tooltip.style.width = "100%";
+ //FormattedTextBoxComment.tooltip.style.width = "100%";
FormattedTextBoxComment.tooltip.style.height = "100%";
FormattedTextBoxComment.tooltip.style.overflow = "hidden";
FormattedTextBoxComment.tooltip.style.display = "none";
@@ -117,7 +117,7 @@ export class FormattedTextBoxComment {
textBox.props.addDocTab(linkDoc, e.ctrlKey ? "add" : "add:right");
} else {
const target = LinkManager.getOppositeAnchor(linkDoc, textBox.dataDoc);
- target && LinkMenuItem.followDefault(linkDoc, textBox.dataDoc, target, textBox.props.addDocTab);
+ target && DocumentView.followLinkClick(linkDoc, textBox.dataDoc, textBox.props, e.shiftKey, e.altKey);
}
}
}
@@ -234,6 +234,7 @@ export class FormattedTextBoxComment {
FormattedTextBoxComment.tooltip.removeChild(FormattedTextBoxComment.tooltipText);
} catch (e) { }
FormattedTextBoxComment.tooltipText = document.createElement("div");
+ FormattedTextBoxComment.tooltipText.className = "FormattedTextBoxComment-toolTipText";
FormattedTextBoxComment.tooltipText.style.width = "100%";
FormattedTextBoxComment.tooltipText.style.height = "100%";
FormattedTextBoxComment.tooltipText.style.textOverflow = "ellipsis";
@@ -255,11 +256,11 @@ export class FormattedTextBoxComment {
docTarget && DocServer.GetRefField(docTarget).then(async linkDoc => {
if (linkDoc instanceof Doc) {
(FormattedTextBoxComment.tooltipText as any).href = href;
- FormattedTextBoxComment.linkDoc = linkDoc;
+ FormattedTextBoxComment.linkDoc = DocListCast(textBox.props.Document.links).find(link => link.anchor1 === textBox.props.Document || link.anchor2 === textBox.props.Document ? link : undefined) || linkDoc;
const anchor = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.dataDoc) ? Cast(linkDoc.anchor2, Doc) : (Cast(linkDoc.anchor1, Doc)) || linkDoc);
const target = anchor?.annotationOn ? await DocCastAsync(anchor.annotationOn) : anchor;
if (anchor !== target && anchor && target) {
- target._scrollY = NumCast(anchor?.y);
+ target._scrollPreviewY = NumCast(anchor?.y);
}
if (target?.author) {
FormattedTextBoxComment.showCommentbox("", view, nbef);
@@ -308,6 +309,7 @@ export class FormattedTextBoxComment {
pinToPres={returnFalse}
dontRegisterView={true}
docFilters={returnEmptyFilter}
+ docRangeFilters={returnEmptyFilter}
searchFilterDocs={returnEmptyDoclist}
ContainingCollectionDoc={undefined}
ContainingCollectionView={undefined}
@@ -318,8 +320,8 @@ export class FormattedTextBoxComment {
whenActiveChanged={returnFalse}
bringToFront={returnFalse}
ContentScaling={returnOne}
- NativeWidth={target._nativeWidth ? (() => NumCast(target._nativeWidth)) : undefined}
- NativeHeight={target._natvieHeight ? (() => NumCast(target._nativeHeight)) : undefined}
+ NativeWidth={Doc.NativeWidth(target) ? (() => Doc.NativeWidth(target)) : undefined}
+ NativeHeight={Doc.NativeHeight(target) ? (() => Doc.NativeHeight(target)) : undefined}
/>
</div>
</div>;
@@ -328,7 +330,7 @@ export class FormattedTextBoxComment {
ReactDOM.render(docPreview, FormattedTextBoxComment.tooltipText);
- FormattedTextBoxComment.tooltip.style.width = "100%";
+ //FormattedTextBoxComment.tooltip.style.width = "100%";
FormattedTextBoxComment.tooltip.style.height = "100%";
}
}
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 68239a8f1..cf9b03308 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -31,8 +31,9 @@ const { toggleMark } = require("prosemirror-commands");
export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
static Instance: RichTextMenu;
public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable
+ private _linkToRef = React.createRef<HTMLInputElement>();
- public view?: EditorView;
+ @observable public view?: EditorView;
public editorProps: FieldViewProps & FormattedTextBoxProps | undefined;
public _brushMap: Map<string, Set<Mark>> = new Map();
@@ -153,11 +154,14 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
public delayHide = () => this._delayHide = true;
@action
- public updateMenu(view: EditorView, lastState: EditorState | undefined, props: any) {
- if (!view || !(view as any).TextView?.props.isSelected(true) || !view.hasFocus()) {
+ public updateMenu(view: EditorView | undefined, lastState: EditorState | undefined, props: any) {
+ if (this._linkToRef.current?.getBoundingClientRect().width) {
return;
}
this.view = view;
+ if (!view || !view.hasFocus()) {
+ return;
+ }
props && (this.editorProps = props);
// Don't do anything if the document/selection didn't change
@@ -366,7 +370,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
);
}
- createMarksDropdown(activeOption: string, options: { mark: Mark | null, title: string, label: string, command: (mark: Mark, view: EditorView) => void, hidden?: boolean, style?: {} }[], key: string, setter: (val: string) => {}): JSX.Element {
+ createMarksDropdown(activeOption: string, options: { mark: Mark | null, title: string, label: string, command: (mark: Mark, view: EditorView) => void, hidden?: boolean, style?: {} }[], key: string, setter: (val: string) => void): JSX.Element {
const items = options.map(({ title, label, hidden, style }) => {
if (hidden) {
return <option value={label} title={title} key={label} style={style ? style : {}} hidden>{label}</option>;
@@ -378,19 +382,22 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
function onChange(e: React.ChangeEvent<HTMLSelectElement>) {
e.stopPropagation();
e.preventDefault();
- self.TextView.endUndoTypingBatch();
- options.forEach(({ label, mark, command }) => {
- if (e.target.value === label && mark) {
- if (!self.TextView.props.isSelected(true)) {
- switch (mark.type) {
- case schema.marks.pFontFamily: setter(Doc.UserDoc().fontFamily = mark.attrs.family); break;
- case schema.marks.pFontSize: setter(Doc.UserDoc().fontSize = mark.attrs.fontSize.toString() + "px"); break;
+ self.TextView?.endUndoTypingBatch();
+ UndoManager.RunInBatch(() => {
+ options.forEach(({ label, mark, command }) => {
+ if (e.target.value === label && mark) {
+ if (!self.TextView?.props.isSelected(true)) {
+ switch (mark.type) {
+ case schema.marks.pFontFamily: setter(Doc.UserDoc().fontFamily = mark.attrs.family); break;
+ case schema.marks.pFontSize: setter(Doc.UserDoc().fontSize = mark.attrs.fontSize.toString() + "px"); break;
+ }
}
+ else self.view && mark && command(mark, self.view);
}
- else UndoManager.RunInBatch(() => self.view && mark && command(mark, self.view), "text mark dropdown");
- }
- });
+ });
+ }, "text mark dropdown");
}
+
return <Tooltip key={key} title={<div className="dash-tooltip">{key}</div>} placement="bottom">
<select onChange={onChange} value={activeOption}>{items}</select>
</Tooltip>;
@@ -424,9 +431,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
}
changeFontSize = (mark: Mark, view: EditorView) => {
- if ((this.view?.state.selection.$from.pos || 0) < 2) {
- this.TextView.layoutDoc._fontSize = mark.attrs.fontSize === Number(mark.attrs.fontSize) ? `${mark.attrs.fontSize}pt` : mark.attrs.fontSize;
- }
const fmark = view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize });
this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true);
view.focus();
@@ -434,9 +438,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
}
changeFontFamily = (mark: Mark, view: EditorView) => {
- if ((this.view?.state.selection.$from.pos || 0) < 2) {
- this.TextView.layoutDoc._fontFamily = mark.attrs.family;
- }
const fmark = view.state.schema.marks.pFontFamily.create({ family: mark.attrs.family });
this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true);
view.focus();
@@ -795,7 +796,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
const self = this;
function onLinkChange(e: React.ChangeEvent<HTMLInputElement>) {
- self.TextView.endUndoTypingBatch();
+ self.TextView?.endUndoTypingBatch();
UndoManager.RunInBatch(() => self.setCurrentLink(e.target.value), "link change");
}
@@ -810,7 +811,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
const dropdownContent =
<div className="dropdown link-menu">
<p>Linked to:</p>
- <input value={link} placeholder="Enter URL" onChange={onLinkChange} />
+ <input value={link} ref={this._linkToRef} placeholder="Enter URL" onChange={onLinkChange} />
<button className="make-button" onPointerDown={e => this.makeLinkToURL(link, "add:right")}>Apply hyperlink</button>
<div className="divider"></div>
<button className="remove-button" onPointerDown={e => this.deleteLink()}>Remove link</button>
@@ -984,8 +985,14 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
{this.collapsed ? this.getDragger() : (null)}
<div key="row 2" style={{ display: this.collapsed ? "none" : undefined }}>
<div className="richTextMenu-divider" key="divider 3" />,
- {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => this.activeFontSize = val)),
- this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => this.activeFontFamily = val)),
+ {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => {
+ this.activeFontSize = val;
+ SelectionManager.SelectedDocuments().map(dv => dv.props.Document._fontSize = val);
+ })),
+ this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => {
+ this.activeFontFamily = val;
+ SelectionManager.SelectedDocuments().map(dv => dv.props.Document._fontFamily = val);
+ })),
<div className="richTextMenu-divider" key="divider 4" />,
this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})),
this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer),
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 921c0e128..99334b6db 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -3,7 +3,8 @@ import { NodeSelection, TextSelection } from "prosemirror-state";
import { DataSym, Doc } from "../../../../fields/Doc";
import { Id } from "../../../../fields/FieldSymbols";
import { ComputedField } from "../../../../fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../../../fields/Types";
+import { NumCast, StrCast } from "../../../../fields/Types";
+import { normalizeEmail } from "../../../../fields/util";
import { returnFalse, Utils } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { Docs, DocUtils } from "../../../documents/Documents";
@@ -11,7 +12,6 @@ import { FormattedTextBox } from "./FormattedTextBox";
import { wrappingInputRule } from "./prosemirrorPatches";
import { RichTextMenu } from "./RichTextMenu";
import { schema } from "./schema_rts";
-import { List } from "../../../../fields/List";
export class RichTextRules {
public Document: Doc;
@@ -267,11 +267,11 @@ export class RichTextRules {
// [[fieldKey]] => show field
// [[fieldKey:Doc]] => show field of doc
new InputRule(
- new RegExp(/\[\[([a-zA-Z_@\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@\.\? \-0-9]+)?\]\]$/),
+ new RegExp(/\[\[([a-zA-Z_\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@:\.\? \-0-9]+)?\]\]$/),
(state, match, start, end) => {
const fieldKey = match[1];
- const rawdocid = match[3]?.substring(1);
- const docid = rawdocid ? (!rawdocid.includes("@") ? Doc.CurrentUserEmail + "@" + rawdocid : rawdocid).replace(".", "_") : undefined;
+ const rawdocid = match[3];
+ const docid = rawdocid ? normalizeEmail((!rawdocid.includes("@") ? Doc.CurrentUserEmail + rawdocid : rawdocid.substring(1))) : undefined;
const value = match[2]?.substring(1);
if (!fieldKey) {
const linkId = Utils.GenerateGuid();
@@ -304,7 +304,7 @@ export class RichTextRules {
const fieldKey = match[1] || "";
const fieldParam = match[2]?.replace("…", "...") || "";
const rawdocid = match[3]?.substring(1);
- const docid = rawdocid ? (!rawdocid.includes("@") ? Doc.CurrentUserEmail + "@" + rawdocid : rawdocid).replace(".", "_") : undefined;
+ const docid = rawdocid ? (!rawdocid.includes("@") ? normalizeEmail(Doc.CurrentUserEmail) + "@" + rawdocid : rawdocid) : undefined;
if (!fieldKey && !docid) return state.tr;
docid && DocServer.GetRefField(docid).then(docx => {
if (!(docx instanceof Doc && docx)) {
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index 962085f0d..40c1d1cac 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -31,7 +31,7 @@ export class DashDocView {
}
//moved
- contentScaling = () => NumCast(this._dashDoc!._nativeWidth) > 0 ? this._dashDoc![WidthSym]() / NumCast(this._dashDoc!._nativeWidth) : 1;
+ contentScaling = () => Doc.NativeWidth(this._dashDoc) > 0 ? this._dashDoc![WidthSym]() / Doc.NativeWidth(this._dashDoc) : 1;
//moved
outerFocus = (target: Doc) => this._textBox.props.focus(this._textBox.props.Document); // ideally, this would scroll to show the focus target
@@ -154,6 +154,7 @@ export class DashDocView {
bringToFront={emptyFunction}
dontRegisterView={false}
docFilters={this._textBox.props.docFilters}
+ docRangeFilters={this._textBox.props.docRangeFilters}
searchFilterDocs={this._textBox.props.searchFilterDocs}
ContainingCollectionView={this._textBox.props.ContainingCollectionView}
ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}