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.tsx19
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.scss4
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx41
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss62
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx474
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.scss5
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx177
-rw-r--r--src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts92
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx80
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts35
-rw-r--r--src/client/views/nodes/formattedText/RichTextSchema.tsx20
-rw-r--r--src/client/views/nodes/formattedText/marks_rts.ts23
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts4
-rw-r--r--src/client/views/nodes/formattedText/prosemirrorPatches.js6
14 files changed, 542 insertions, 500 deletions
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 145ee8c2e..123946dea 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -5,13 +5,14 @@ import { Id } from "../../../../fields/FieldSymbols";
import { ObjectField } from "../../../../fields/ObjectField";
import { ComputedField } from "../../../../fields/ScriptField";
import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, Utils, returnZero, returnEmptyFilter } from "../../../../Utils";
+import { emptyFunction, returnEmptyString, returnFalse, Utils, returnZero, returnEmptyFilter, returnEmptyDoclist } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { Docs, DocUtils } from "../../../documents/Documents";
import { DocumentView } from "../DocumentView";
import { FormattedTextBox } from "./FormattedTextBox";
import { Transform } from "../../../util/Transform";
import React = require("react");
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
interface IDashDocView {
node: any;
@@ -131,14 +132,6 @@ export class DashDocView extends React.Component<IDashDocView> {
if (!Doc.AreProtosEqual(finalLayout, dashDoc)) {
finalLayout.rootDocument = dashDoc.aliasOf;
}
- const layoutKey = StrCast(finalLayout.layoutKey);
- const finalKey = layoutKey && StrCast(finalLayout[layoutKey]).split("'")?.[1];
- if (finalLayout !== dashDoc && finalKey) {
- const finalLayoutField = finalLayout[finalKey];
- if (finalLayoutField instanceof ObjectField) {
- finalLayout[finalKey + "-textTemplate"] = ComputedField.MakeFunction(`copyField(this.${finalKey})`, { this: Doc.name });
- }
- }
this._finalLayout = finalLayout;
this._resolvedDataDoc = Cast(finalLayout.resolvedDataDoc, Doc, null);
return { finalLayout, resolvedDataDoc: Cast(finalLayout.resolvedDataDoc, Doc, null) };
@@ -175,7 +168,7 @@ export class DashDocView extends React.Component<IDashDocView> {
const outerStyle = {
position: "relative" as "relative",
textIndent: "0",
- border: "1px solid " + StrCast(this._textBox.Document.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray")),
+ border: "1px solid " + StrCast(this._textBox.Document.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray")),
width: this.props.node.props.width,
height: this.props.node.props.height,
display: this.props.node.props.hidden ? "none" : "inline-block",
@@ -202,7 +195,7 @@ export class DashDocView extends React.Component<IDashDocView> {
({ dim, color }) => {
spanStyle.width = outerStyle.width = Math.max(20, dim[0]) + "px";
spanStyle.height = outerStyle.height = Math.max(20, dim[1]) + "px";
- outerStyle.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+ outerStyle.border = "1px solid " + StrCast(finalLayout.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray"));
}, { fireImmediately: true });
if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") {
@@ -244,8 +237,6 @@ export class DashDocView extends React.Component<IDashDocView> {
addDocTab={this._textBox.props.addDocTab}
pinToPres={returnFalse}
renderDepth={self._textBox.props.renderDepth + 1}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
PanelWidth={finalLayout[WidthSym]}
PanelHeight={finalLayout[HeightSym]}
focus={this.outerFocus}
@@ -255,6 +246,8 @@ 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}
ContentScaling={this.contentScaling}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.scss b/src/client/views/nodes/formattedText/DashFieldView.scss
index 23cf1e79b..e16036000 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.scss
+++ b/src/client/views/nodes/formattedText/DashFieldView.scss
@@ -18,7 +18,8 @@
.dashFieldView-labelSpan {
position: relative;
display: inline-block;
- font-size: small;
+ font-weight: normal;
+ background: rgba(0,0,0,0.1);
}
.dashFieldView-fieldSpan {
min-width: 20px;
@@ -27,6 +28,7 @@
position: relative;
display: inline;
background-color: rgba(155, 155, 155, 0.24);
+ font-weight: bold;
span {
min-width: 100%;
display: inline-block;
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 8ae71c035..b39a845db 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,5 +1,5 @@
-import { IReactionDisposer, observable, runInAction, computed, action } from "mobx";
-import { Doc, DocListCast, Field } from "../../../../fields/Doc";
+import { IReactionDisposer, observable, computed, action } from "mobx";
+import { Doc, DocListCast, Field, DataSym } from "../../../../fields/Doc";
import { List } from "../../../../fields/List";
import { listSpec } from "../../../../fields/Schema";
import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
@@ -70,7 +70,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
DocServer.GetRefField(this.props.docid).
then(action(async dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc)));
} else {
- this._dashDoc = this.props.tbox.props.DataDoc || this.props.tbox.dataDoc;
+ this._dashDoc = this.props.tbox.rootDoc;
}
}
componentWillUnmount() {
@@ -82,7 +82,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
// set the display of the field's value (checkbox for booleans, span of text for strings)
@computed get fieldValueContent() {
if (this._dashDoc) {
- const dashVal = this._dashDoc[this._fieldKey] || (this._fieldKey === "PARAMS" ? this._textBoxDoc[this._fieldKey] : "");
+ const dashVal = this._dashDoc[DataSym][this._fieldKey] ?? this._dashDoc[this._fieldKey] ?? (this._fieldKey === "PARAMS" ? this._textBoxDoc[this._fieldKey] : "");
const fval = dashVal instanceof List ? dashVal.join(this.multiValueDelimeter) : StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(this._textBoxDoc)[this._fieldKey] : dashVal;
const boolVal = Cast(fval, "boolean", null);
const strVal = Field.toString(fval as Field) || "";
@@ -92,7 +92,10 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
return <input
className="dashFieldView-fieldCheck"
type="checkbox" checked={boolVal}
- onChange={e => this._dashDoc![this._fieldKey] = e.target.checked}
+ onChange={e => {
+ if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = e.target.checked;
+ Doc.SetInPlace(this._dashDoc!, this._fieldKey, e.target.checked, true);
+ }}
/>;
}
else // field value is a string, so display it as an editable span
@@ -106,7 +109,10 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
ref={r => {
r?.addEventListener("keydown", e => this.fieldSpanKeyDown(e, r));
r?.addEventListener("blur", e => r && this.updateText(r.textContent!, false));
- r?.addEventListener("pointerdown", action((e) => this._showEnumerables = true));
+ r?.addEventListener("pointerdown", action((e) => {
+ this._showEnumerables = true;
+ e.stopPropagation();
+ }));
}} >
{strVal}
</span>;
@@ -149,16 +155,23 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
if (modText) {
// elementfieldSpan.innerHTML = this._dashDoc![this._fieldKey as string] = modText;
DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, []);
- this._dashDoc![this._fieldKey] = modText;
+ Doc.SetInPlace(this._dashDoc!, this._fieldKey, modText, true);
} // if the text starts with a ':=' then treat it as an expression by making a computed field from its value storing it in the key
else if (nodeText.startsWith(":=")) {
- this._dashDoc![this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(2));
+ this._dashDoc![DataSym][this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(2));
} else if (nodeText.startsWith("=:=")) {
Doc.Layout(this._textBoxDoc)[this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(3));
} else {
- const splits = newText.split(this.multiValueDelimeter);
- if (this._fieldKey !== "PARAMS" || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS) {
- this._dashDoc![this._fieldKey] = splits.length > 1 ? new List<string>(splits) : newText;
+ if (Number(newText).toString() === newText) {
+ if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = Number(newText);
+ Doc.SetInPlace(this._dashDoc!, this._fieldKey, newText, true);
+ } else {
+ const splits = newText.split(this.multiValueDelimeter);
+ if (this._fieldKey !== "PARAMS" || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS) {
+ const strVal = splits.length > 1 ? new List<string>(splits) : newText;
+ if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = strVal;
+ Doc.SetInPlace(this._dashDoc!, this._fieldKey, strVal, true);
+ }
}
}
});
@@ -169,7 +182,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
onPointerDownEnumerables = async (e: any) => {
e.stopPropagation();
const collview = await DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, [{ title: this._fieldKey }]);
- collview instanceof Doc && this.props.tbox.props.addDocTab(collview, "onRight");
+ collview instanceof Doc && this.props.tbox.props.addDocTab(collview, "add:right");
}
@@ -183,7 +196,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
}
if (container) {
const alias = Doc.MakeAlias(container.props.Document);
- alias.viewType = CollectionViewType.Time;
+ alias._viewType = CollectionViewType.Time;
let list = Cast(alias._columnHeaders, listSpec(SchemaHeaderField));
if (!list) {
alias._columnHeaders = list = new List<SchemaHeaderField>();
@@ -191,7 +204,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, "#f1efeb"));
list.map(c => c.heading).indexOf("text") === -1 && list.push(new SchemaHeaderField("text", "#f1efeb"));
alias._pivotField = this._fieldKey.startsWith("#") ? "#" : this._fieldKey;
- this.props.tbox.props.addDocTab(alias, "onRight");
+ this.props.tbox.props.addDocTab(alias, "add:right");
}
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index afdd8fea2..dbf98a5e9 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -32,8 +32,8 @@
.formattedTextBox-dictation {
height: 12px;
width: 10px;
- top: 0px;
- left: 0px;
+ bottom: 5px;
+ right: 8px;
position: absolute;
}
}
@@ -43,24 +43,21 @@
overflow: auto;
display: inline-block;
width: 100%;
- height: 100%;
+ height: unset;
}
-
+
.formattedTextBox-sidebar-handle {
position: absolute;
- top: calc(50% - 17.5px);
+ top: 0;
+ //top: calc(50% - 17.5px); // use this to center vertically -- make sure it looks okay for slide views
width: 10px;
- height: 35px;
+ height: 100%;
+ max-height: 35px;
background: lightgray;
border-radius: 20px;
cursor:grabbing;
}
-.formattedTextBox-cont>.formattedTextBox-sidebar-handle {
- right: 0;
- left: unset;
-}
-
.formattedTextBox-sidebar,
.formattedTextBox-sidebar-inking {
border-left: dashed 1px black;
@@ -73,11 +70,6 @@
.collectionfreeformview-container {
position: relative;
}
-
- >.formattedTextBox-sidebar-handle {
- right: unset;
- left: -5;
- }
}
.formattedTextBox-sidebar-inking {
@@ -94,7 +86,9 @@
}
.formattedTextBox-inner-rounded, .formattedTextBox-inner-rounded-selected,
-.formattedTextBox-inner, .formattedTextBox-inner-selected {
+.formattedTextBox-inner,
+.formattedTextBox-inner-minimal,
+.formattedTextBox-inner-selected {
height: 100%;
white-space: pre-wrap;
.ProseMirror:hover {
@@ -112,6 +106,15 @@
border-width: 1px;
}
}
+.formattedTextBox-inner-rounded-selected,
+.formattedTextBox-inner-selected {
+ .ProseMirror {
+ padding:10px;
+ }
+ .ProseMirror:hover {
+ background: unset;
+ }
+}
// .menuicon {
// display: inline-block;
@@ -338,15 +341,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";
@@ -396,10 +390,11 @@ footnote::after {
width: 100%;
height: 100%;
}
-
+
.formattedTextBox-sidebar-handle {
position: absolute;
- top: calc(50% - 17.5px);
+ top: 0;
+ //top: calc(50% - 17.5px); // use this to center vertically -- make sure it looks okay for slide views
width: 10px;
height: 35px;
background: lightgray;
@@ -407,11 +402,6 @@ footnote::after {
cursor:grabbing;
}
- .formattedTextBox-cont>.formattedTextBox-sidebar-handle {
- right: 0;
- left: unset;
- }
-
.formattedTextBox-sidebar,
.formattedTextBox-sidebar-inking {
border-left: dashed 1px black;
@@ -423,11 +413,6 @@ footnote::after {
.collectionfreeformview-container {
position: relative;
}
-
- >.formattedTextBox-sidebar-handle {
- right: unset;
- left: -5;
- }
}
.formattedTextBox-sidebar-inking {
@@ -443,6 +428,7 @@ footnote::after {
left: 10%;
}
+ .formattedTextBox-inner-minimal,
.formattedTextBox-inner-rounded,
.formattedTextBox-inner {
height: 100%;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index d4c9f74d5..a998386d8 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1,7 +1,5 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faEdit, faSmile, faTextHeight, faUpload } from '@fortawesome/free-solid-svg-icons';
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";
@@ -17,15 +15,15 @@ import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, Wid
import { documentSchema } from '../../../../fields/documentSchemas';
import applyDevTools = require("prosemirror-dev-tools");
import { removeMarkWithAttrs } from "./prosemirrorPatches";
-import { Id } from '../../../../fields/FieldSymbols';
+import { Id, Copy } from '../../../../fields/FieldSymbols';
import { InkTool } from '../../../../fields/InkField';
import { PrefetchProxy } from '../../../../fields/Proxy';
import { RichTextField } from "../../../../fields/RichTextField";
import { RichTextUtils } from '../../../../fields/RichTextUtils';
-import { createSchema, makeInterface } from "../../../../fields/Schema";
+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 } from '../../../../Utils';
+import { TraceMobx, OVERRIDE_acl, GetEffectiveAcl } from '../../../../fields/util';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents, OmitKeys } from '../../../../Utils';
import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
import { DocServer } from "../../../DocServer";
import { Docs, DocUtils } from '../../../documents/Documents';
@@ -33,8 +31,8 @@ import { DocumentType } from '../../../documents/DocumentTypes';
import { DictationManager } from '../../../util/DictationManager';
import { DragManager } from "../../../util/DragManager";
import { makeTemplate } from '../../../util/DropConverter';
-import buildKeymap, { updateBullets } from "./ProsemirrorExampleTransfer";
-import RichTextMenu, { RichTextMenuPlugin } from './RichTextMenu';
+import { buildKeymap, updateBullets } from "./ProsemirrorExampleTransfer";
+import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu';
import { RichTextRules } from "./RichTextRules";
//import { DashDocView } from "./DashDocView";
@@ -60,15 +58,16 @@ import "./FormattedTextBox.scss";
import { FormattedTextBoxComment, formattedTextBoxCommentPlugin, findLinkMark } from './FormattedTextBoxComment';
import React = require("react");
import { DocumentManager } from '../../../util/DocumentManager';
-
-library.add(faEdit);
-library.add(faSmile, faTextHeight, faUpload);
+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
hideOnLeave?: boolean; // used by DocumentView for setting caption's hide on leave (bcz: would prefer to have caption-hideOnLeave field set or something similar)
xMargin?: number; // used to override document's settings for xMargin --- see CollectionCarouselView
yMargin?: number;
+ dontSelectOnLoad?: boolean; // suppress selecting the text box when loaded
}
export const GoogleRef = "googleDocId";
@@ -84,6 +83,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
public static Instance: FormattedTextBox;
public ProseRef?: HTMLDivElement;
public get EditorView() { return this._editorView; }
+ private _boxRef: React.RefObject<HTMLDivElement> = React.createRef();
private _ref: React.RefObject<HTMLDivElement> = React.createRef();
private _scrollRef: React.RefObject<HTMLDivElement> = React.createRef();
private _editorView: Opt<EditorView>;
@@ -99,8 +99,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
private _linkTime: number | null = null;
private _pause: boolean = false;
- @computed get _recording() { return this.dataDoc.audioState === "recording"; }
- set _recording(value) { this.dataDoc.audioState = value ? "recording" : undefined; }
+ @computed get _recording() { return this.dataDoc?.audioState === "recording"; }
+ set _recording(value) {
+ this.dataDoc.audioState = value ? "recording" : undefined;
+ }
@observable private _entered = false;
@@ -225,7 +227,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
const allLinks = [{ href: Utils.prepend("/doc/" + id), title: value, targetId: id }];
- const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, location: "onRight", title: value });
+ const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, location: "add:right", title: value });
const mval = this._editorView.state.schema.marks.metadataVal.create();
const offset = (tx.selection.to === range!.end - 1 ? -1 : 0);
tx = tx.addMark(textEndSelection - value.length + offset, textEndSelection, link).addMark(textEndSelection - value.length + offset, textEndSelection, mval);
@@ -239,20 +241,23 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const tsel = this._editorView.state.selection.$from;
tsel.marks().filter(m => m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 1000)));
const curText = state.doc.textBetween(0, state.doc.content.size, " \n");
- const curTemp = Cast(this.layoutDoc[this.props.fieldKey + "-textTemplate"], RichTextField); // the actual text in the text box
+ const curTemp = this.layoutDoc.resolvedDataDoc ? Cast(this.layoutDoc[this.props.fieldKey], RichTextField) : undefined; // the actual text in the text box
const curProto = Cast(Cast(this.dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype
const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template
const json = JSON.stringify(state.toJSON());
let unchanged = true;
const effectiveAcl = GetEffectiveAcl(this.dataDoc);
+ const removeSelection = (json: string | undefined) => {
+ return json?.indexOf("\"storedMarks\"") === -1 ? json?.replace(/"selection":.*/, "") : json?.replace(/"selection":"\"storedMarks\""/, "\"storedMarks\"");
+ };
if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) {
- if (!this._applyingChange && json.replace(/"selection":.*/, "") !== curProto?.Data.replace(/"selection":.*/, "")) {
+ if (!this._applyingChange && removeSelection(json) !== removeSelection(curProto?.Data)) {
this._applyingChange = true;
(curText !== Cast(this.dataDoc[this.fieldKey], RichTextField)?.Text) && (this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())));
- if ((!curTemp && !curProto) || curText || curLayout?.Data.includes("dash")) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
- if (json.replace(/"selection":.*/, "") !== curLayout?.Data.replace(/"selection":.*/, "")) {
+ if ((!curTemp && !curProto) || curText || json.includes("dash")) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
+ if (removeSelection(json) !== removeSelection(curLayout?.Data)) {
if (!this._pause && !this.layoutDoc._timeStampOnEnter) {
timeStamp = setTimeout(() => this.pause(), 10 * 1000); // 10 seconds delay for time stamp
}
@@ -265,7 +270,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
!curText && tx.storedMarks?.map(m => m.type.name === "pFontSize" && (Doc.UserDoc().fontSize = this.layoutDoc._fontSize = m.attrs.fontSize));
!curText && tx.storedMarks?.map(m => m.type.name === "pFontFamily" && (Doc.UserDoc().fontFamily = this.layoutDoc._fontFamily = m.attrs.fontFamily));
this.dataDoc[this.props.fieldKey] = new RichTextField(json, curText);
- this.dataDoc[this.props.fieldKey + "-noTemplate"] = (curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
+ this.dataDoc[this.props.fieldKey + "-noTemplate"] = true;//(curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText });
unchanged = false;
@@ -349,8 +354,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) {
+ if (!this.props.dontRegisterView && // (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.dataDoc["title-custom"] &&
+ (Doc.LayoutFieldKey(this.rootDoc) === this.fieldKey || this.fieldKey === "text")) {
let node = this._editorView.state.doc;
while (node.firstChild && node.firstChild.type.name !== "text") node = node.firstChild;
const str = node.textContent;
@@ -374,10 +380,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this._editorView.dispatch(tr.addMark(flattened[lastSel].from, flattened[lastSel].to, link));
}
}
- public highlightSearchTerms = (terms: string[], alt: boolean) => {
+ public highlightSearchTerms = (terms: string[], backward: boolean) => {
if (this._editorView && (this._editorView as any).docView && terms.some(t => t)) {
-
-
const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true });
const res = terms.filter(t => t).map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term));
@@ -385,24 +389,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
let tr = this._editorView.state.tr;
const flattened: TextSelection[] = [];
res.map(r => r.map(h => flattened.push(h)));
- if (BoolCast(Doc.GetProto(this.dataDoc).resetSearch) === true) {
- this._searchIndex = 0;
- Doc.GetProto(this.dataDoc).resetSearch = undefined;
- }
- else {
- this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
- if (alt === true) {
- if (this._searchIndex > 1) {
- this._searchIndex += -2;
- }
- else if (this._searchIndex === 1) {
- this._searchIndex = length - 1;
- }
- else if (this._searchIndex === 0 && length !== 1) {
- this._searchIndex = length - 2;
- }
-
+ this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
+ if (backward === true) {
+ if (this._searchIndex > 1) {
+ this._searchIndex += -2;
+ }
+ else if (this._searchIndex === 1) {
+ this._searchIndex = length - 1;
+ }
+ else if (this._searchIndex === 0 && length !== 1) {
+ this._searchIndex = length - 2;
}
+
}
const lastSel = Math.min(flattened.length - 1, this._searchIndex);
@@ -470,12 +468,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
} else if (de.complete.linkDragData) {
de.complete.linkDragData.linkDropCallback = this.linkDrop;
}
+ else if (de.complete.annoDragData) {
+ de.complete.annoDragData.linkDropCallback = this.linkDrop;
+ }
}
- linkDrop = (data: DragManager.LinkDragData) => {
+ linkDrop = (data: { linkDocument?: Doc }) => {
const linkDoc = data.linkDocument!;
const anchor1Title = linkDoc.anchor1 instanceof Doc ? StrCast(linkDoc.anchor1.title) : "-untitled-";
const anchor1Id = linkDoc.anchor1 instanceof Doc ? linkDoc.anchor1[Id] : "";
- this.makeLinkToSelection(linkDoc[Id], anchor1Title, "onRight", anchor1Id);
+ this.makeLinkToSelection(linkDoc[Id], anchor1Title, "add:right", anchor1Id);
}
getNodeEndpoints(context: Node, node: Node): { from: number, to: number } | null {
@@ -509,10 +510,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
if (node.isTextblock) {
let index = 0, foundAt;
const ep = this.getNodeEndpoints(pm.state.doc, node);
- while (ep && (foundAt = node.textContent.slice(index).search(RegExp(find, "i"))) > -1) {
- const sel = new TextSelection(pm.state.doc.resolve(ep.from + index + foundAt + 1), pm.state.doc.resolve(ep.from + index + foundAt + find.length + 1));
- ret.push(sel);
- index = index + foundAt + find.length;
+ const regexp = new RegExp(find.replace("*", ""), "i");
+ if (regexp) {
+ while (ep && (foundAt = node.textContent.slice(index).search(regexp)) > -1) {
+ const sel = new TextSelection(pm.state.doc.resolve(ep.from + index + foundAt + 1), pm.state.doc.resolve(ep.from + index + foundAt + find.length + 1));
+ ret.push(sel);
+ index = index + foundAt + find.length;
+ }
}
} else {
node.content.forEach((child, i) => ret = ret.concat(this.findInNode(pm, child, find)));
@@ -556,17 +560,22 @@ 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 * (1 - (e.clientX - bounds.left) / bounds.width) + "%";
+ this.layoutDoc._sidebarWidthPercent = "" + 100 * Math.max(0, (1 - (e.clientX - bounds.left) / bounds.width)) + "%";
+ this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== "0%";
return false;
}
@undoBatch
@action
toggleNativeDimensions = () => {
- Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth(), this.props.NativeHeight());
+ Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth?.() || 0, this.props.NativeHeight?.() || 0);
}
public static get DefaultLayout(): Doc | string | undefined {
@@ -576,6 +585,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const cm = ContextMenu.Instance;
const changeItems: ContextMenuProps[] = [];
+ changeItems.push({ description: "plain", event: undoBatch(() => Doc.setNativeView(this.rootDoc)), icon: "eye" });
const noteTypesDoc = Cast(Doc.UserDoc()["template-notes"], Doc, null);
DocListCast(noteTypesDoc?.data).forEach(note => {
changeItems.push({
@@ -585,9 +595,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}), icon: "eye"
});
});
- changeItems.push({ description: "FreeForm", event: () => DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" });
+ !Doc.UserDoc().noviceMode && changeItems.push({ description: "FreeForm", event: () => DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" });
const highlighting: ContextMenuProps[] = [];
- ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
+ const noviceHighlighting = ["My Text", "Text from Others"];
+ const expertHighlighting = ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"];
+ (Doc.UserDoc().noviceMode ? noviceHighlighting : expertHighlighting).forEach(option =>
highlighting.push({
description: (FormattedTextBox._highlights.indexOf(option) === -1 ? "Highlight " : "Unhighlight ") + option, event: () => {
e.stopPropagation();
@@ -602,10 +614,9 @@ 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" });
- uicontrols.push({ description: `Create TimeStamp When ${this.layoutDoc._timeStampOnEnter ? "Pause" : "Enter"}`, event: () => this.layoutDoc._timeStampOnEnter = !this.layoutDoc._timeStampOnEnter, icon: "expand-arrows-alt" });
+ !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" });
!Doc.UserDoc().noviceMode && uicontrols.push({
description: "Broadcast Message", event: () => DocServer.GetRefField("rtfProto").then(proto =>
proto instanceof Doc && (proto.BROADCAST_MESSAGE = Cast(this.rootDoc[this.fieldKey], RichTextField)?.Text)), icon: "expand-arrows-alt"
@@ -615,15 +626,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const appearance = cm.findByDescription("Appearance...");
const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
appearanceItems.push({ description: "Change Perspective...", noexpand: true, subitems: changeItems, icon: "external-link-alt" });
- this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
- Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
- appearanceItems.push({
- description: "Convert to be a template style", event: () => {
+ // this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
+ !Doc.UserDoc().noviceMode && appearanceItems.push({
+ description: "Make Default Layout", event: () => {
if (!this.layoutDoc.isTemplateDoc) {
const title = StrCast(this.rootDoc.title);
this.rootDoc.title = "text";
this.rootDoc.isTemplateDoc = makeTemplate(this.rootDoc, true, title);
- } else {
+ } else if (!this.rootDoc.isTemplateDoc) {
const title = StrCast(this.rootDoc.title);
this.rootDoc.title = "text";
this.rootDoc.layout = (this.layoutDoc as Doc).layout as string;
@@ -637,12 +647,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.rootDoc._width = this.layoutDoc._width || 300; // are stored on the template, since we're getting rid of the old template
this.rootDoc._height = this.layoutDoc._height || 200; // we need to copy them over to the root. This should probably apply to all '_' fields
this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, "string", null);
+ this.rootDoc.backgroundColor = Cast(this.layoutDoc.backgroundColor, "string", null);
}, 10);
}
+ Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc);
Doc.AddDocToList(Cast(Doc.UserDoc()["template-notes"], Doc, null), "data", this.rootDoc);
}, icon: "eye"
});
- appearanceItems.push({ description: "Create progressivized slide...", event: this.progressivizeText, icon: "desktop" });
+ !Doc.UserDoc().noviceMode && appearanceItems.push({ description: "Create progressivized slide...", event: this.progressivizeText, icon: "desktop" });
cm.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" });
const options = cm.findByDescription("Options...");
@@ -662,7 +674,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const newBullets: Doc[] = this.recursiveProgressivize(1, list)[0];
mainBulletList.push.apply(mainBulletList, newBullets);
}
- console.log(mainBulletList.length);
const title = Docs.Create.TextDocument(StrCast(this.rootDoc.title), { title: "Title", _width: 800, _height: 70, x: 20, y: -10, _fontSize: '20pt', backgroundColor: "rgba(0,0,0,0)", appearFrame: 0, _fontWeight: 700 });
mainBulletList.push(title);
const doc = Docs.Create.FreeformDocument(mainBulletList, {
@@ -717,7 +728,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
recordDictation = () => {
DictationManager.Controls.listen({
- interimHandler: this.setCurrentBulletContent,
+ interimHandler: this.setDictationContent,
continuous: { indefinite: false },
}).then(results => {
if (results && [DictationManager.Controls.Infringed].includes(results)) {
@@ -728,22 +739,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
stopDictation = (abort: boolean) => { DictationManager.Controls.stop(!abort); };
- recordBullet = async () => {
- const completedCue = "end session";
- const results = await DictationManager.Controls.listen({
- interimHandler: this.setCurrentBulletContent,
- continuous: { indefinite: false },
- terminators: [completedCue, "bullet", "next"]
- });
- if (results && [DictationManager.Controls.Infringed, completedCue].includes(results)) {
- DictationManager.Controls.stop();
- return;
- }
- this.nextBullet(this._editorView!.state.selection.to);
- setTimeout(this.recordBullet, 2000);
- }
-
- setCurrentBulletContent = (value: string) => {
+ setDictationContent = (value: string) => {
if (this._editorView) {
const state = this._editorView.state;
const now = Date.now();
@@ -758,33 +754,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
}
}
- const recordingStart = DateCast(this.props.Document.recordingStart).date.getTime();
- this._break = false;
- value = "" + (mark.attrs.modified * 1000 - recordingStart) / 1000 + value;
const from = state.selection.from;
- const inserted = state.tr.insertText(value).addMark(from, from + value.length + 1, mark);
- this._editorView.dispatch(inserted.setSelection(TextSelection.create(inserted.doc, from, from + value.length + 1)));
- }
- }
-
- nextBullet = (pos: number) => {
- if (this._editorView) {
- const frag = Fragment.fromArray(this.newListItems(2));
- if (this._editorView.state.doc.resolve(pos).depth >= 2) {
- const slice = new Slice(frag, 2, 2);
- let state = this._editorView.state;
- this._editorView.dispatch(state.tr.step(new ReplaceStep(pos, pos, slice)));
- pos += 4;
- state = this._editorView.state;
- this._editorView.dispatch(state.tr.setSelection(TextSelection.create(this._editorView.state.doc, pos, pos)));
+ this._break = false;
+ if (this.props.Document.recordingStart) {
+ const recordingStart = DateCast(this.props.Document.recordingStart)?.date.getTime();
+ value = "" + (mark.attrs.modified * 1000 - recordingStart) / 1000 + value;
}
+ const tr = state.tr.insertText(value).addMark(from, from + value.length + 1, mark);
+ this._editorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, from, from + value.length + 1)));
}
}
- private newListItems = (count: number) => {
- return numberRange(count).map(x => schema.nodes.list_item.create(undefined, schema.nodes.paragraph.create()));
- }
-
_keymap: any = undefined;
_rules: RichTextRules | undefined;
@computed get config() {
@@ -823,9 +803,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
tr = tr.addMark(pos, pos + node.nodeSize, link);
}
});
- OVERRIDE_ACL(true);
+ OVERRIDE_acl(true);
this._editorView!.dispatch(tr.removeMark(sel.from, sel.to, splitter));
- OVERRIDE_ACL(false);
+ OVERRIDE_acl(false);
}
}
componentDidMount() {
@@ -848,19 +828,23 @@ 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] : "";
- this.makeLinkToSelection(linkDoc[Id], anchor2Title, "onRight", anchor2Id);
+ 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);
}
},
{ fireImmediately: true }
);
this._disposers.editorState = reaction(
() => {
- if (this.dataDoc?.[this.props.fieldKey + "-noTemplate"] || !this.layoutDoc[this.props.fieldKey + "-textTemplate"]) {
+ if (!this.dataDoc || !this.layoutDoc) return undefined;
+ if (this.dataDoc?.[this.props.fieldKey + "-noTemplate"] || !this.layoutDoc[this.props.fieldKey]) {
return Cast(this.dataDoc[this.props.fieldKey], RichTextField, null)?.Data;
}
- return Cast(this.layoutDoc[this.props.fieldKey + "-textTemplate"], RichTextField, null)?.Data;
+ return Cast(this.layoutDoc[this.props.fieldKey], RichTextField, null)?.Data;
},
incomingValue => {
if (incomingValue !== undefined && this._editorView && !this._applyingChange) {
@@ -870,7 +854,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.tryUpdateHeight();
}
}
- }
+ },
);
this._disposers.pullDoc = reaction(
() => this.props.Document[Pulls],
@@ -892,13 +876,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
);
this._disposers.autoHeight = reaction(
- () => [this.layoutDoc[WidthSym](), this.layoutDoc._autoHeight],
- () => setTimeout(() => this.tryUpdateHeight(), 0)
+ () => ({
+ width: NumCast(this.layoutDoc._width),
+ autoHeight: this.layoutDoc?._autoHeight
+ }),
+ ({ width, autoHeight }) => width !== undefined && setTimeout(() => this.tryUpdateHeight(), 0)
);
this._disposers.height = reaction(
- () => this.layoutDoc[HeightSym](),
+ () => NumCast(this.layoutDoc._height),
action(height => {
- if (height <= 20 && height < NumCast(this.layoutDoc._delayAutoHeight, 20)) {
+ if (height !== undefined && height <= 20 && height < NumCast(this.layoutDoc._delayAutoHeight, 20)) {
this.layoutDoc._delayAutoHeight = height;
}
})
@@ -906,23 +893,24 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.setupEditor(this.config, this.props.fieldKey);
- this._disposers.searchAlt = reaction(() => this.rootDoc.searchMatchAlt,
- search => search ? this.highlightSearchTerms([Doc.SearchQuery()], false) : this.unhighlightSearchTerms(),
- { fireImmediately: true });
- this._disposers.search = reaction(() => this.rootDoc.searchMatch,
- search => search ? this.highlightSearchTerms([Doc.SearchQuery()], true) : this.unhighlightSearchTerms(),
- { fireImmediately: this.rootDoc.searchMatch ? true : false });
-
- this._disposers.record = reaction(() => this._recording,
- () => {
- if (this._recording) {
- setTimeout(action(() => {
- this.stopDictation(true);
- setTimeout(() => this.recordDictation(), 500);
- }), 500);
- } else setTimeout(() => this.stopDictation(true), 0);
- }
- );
+ this._disposers.search = reaction(() => Doc.IsSearchMatch(this.rootDoc),
+ search => search ? this.highlightSearchTerms([Doc.SearchQuery()], search.searchMatch < 0) : this.unhighlightSearchTerms(),
+ { fireImmediately: Doc.IsSearchMatchUnmemoized(this.rootDoc) ? true : false });
+
+ this._disposers.selected = reaction(() => this.props.isSelected(), action(() => this._recording = false));
+
+ if (!this.props.dontRegisterView) {
+ this._disposers.record = reaction(() => this._recording,
+ () => {
+ if (this._recording) {
+ setTimeout(action(() => {
+ this.stopDictation(true);
+ setTimeout(() => this.recordDictation(), 500);
+ }), 500);
+ } else setTimeout(() => this.stopDictation(true), 0);
+ }
+ );
+ }
this._disposers.scrollToRegion = reaction(
() => StrCast(this.layoutDoc.scrollToLinkID),
async (scrollToLinkID) => {
@@ -932,10 +920,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const examinedNode = findLinkNode(node, editor);
if (examinedNode?.textContent) {
nodes.push(examinedNode);
- start += index;
+ !start && (start = index);
}
});
- return { frag: Fragment.fromArray(nodes), start: start };
+ return { frag: Fragment.fromArray(nodes), start };
};
const findLinkNode = (node: Node, editor: EditorView) => {
if (!node.isText) {
@@ -972,7 +960,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
pos => this._scrollRef.current && this._scrollRef.current.scrollTo({ top: pos }), { fireImmediately: true }
);
- setTimeout(() => this.tryUpdateHeight(NumCast(this.layoutDoc.limitHeight, 0)));
+ setTimeout(() => this.tryUpdateHeight(NumCast(this.layoutDoc.limitHeight)));
}
pushToGoogleDoc = async () => {
@@ -1018,7 +1006,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
if (documentId) {
exportState = await RichTextUtils.GoogleDocs.Import(documentId, dataDoc);
}
- UndoManager.RunInBatch(() => handler(exportState, dataDoc), Pulls);
+ exportState && UndoManager.RunInBatch(() => handler(exportState, dataDoc), Pulls);
}
updateState = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => {
@@ -1034,7 +1022,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
}, 0);
dataDoc.title = exportState.title;
- this.rootDoc.customTitle = true;
+ this.dataDoc["title-custom"] = true;
dataDoc.unchanged = true;
} else {
delete dataDoc[GoogleRef];
@@ -1129,7 +1117,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const marks = [...node.marks];
const linkIndex = marks.findIndex(mark => mark.type.name === "link");
const allLinks = [{ href: Utils.prepend(`/doc/${linkId}`), title, linkId }];
- const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "onRight", title, docref: true });
+ const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "add:right", title, docref: true });
marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link);
return node.mark(marks);
}
@@ -1137,8 +1125,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
private setupEditor(config: any, fieldKey: string) {
const curText = Cast(this.dataDoc[this.props.fieldKey], RichTextField, null);
- const useTemplate = !curText?.Text && this.layoutDoc[this.props.fieldKey + "-textTemplate"];
- const rtfField = Cast((useTemplate && this.layoutDoc[this.props.fieldKey + "-textTemplate"]) || this.dataDoc[fieldKey], RichTextField);
+ const rtfField = Cast((!curText?.Text && this.layoutDoc[this.props.fieldKey]) || this.dataDoc[fieldKey], RichTextField);
if (this.ProseRef) {
const self = this;
this._editorView?.destroy();
@@ -1174,11 +1161,21 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
const selectOnLoad = this.rootDoc[Id] === FormattedTextBox.SelectOnLoad;
- if (selectOnLoad && !this.props.dontRegisterView) {
+ if (selectOnLoad && !this.props.dontRegisterView && !this.props.dontSelectOnLoad) {
FormattedTextBox.SelectOnLoad = "";
this.props.select(false);
- FormattedTextBox.SelectOnLoadChar && this._editorView!.dispatch(this._editorView!.state.tr.insertText(FormattedTextBox.SelectOnLoadChar));
- FormattedTextBox.SelectOnLoadChar = "";
+ if (FormattedTextBox.SelectOnLoadChar && this._editorView) {
+ const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined;
+ 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];
+ 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);
+ this.startUndoTypingBatch();
+ }
}
selectOnLoad && this._editorView!.focus();
@@ -1201,8 +1198,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
componentWillUnmount() {
+ this.endUndoTypingBatch();
Object.values(this._disposers).forEach(disposer => disposer?.());
this._editorView?.destroy();
+ FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none");
}
_downEvent: any;
@@ -1230,7 +1229,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
if (this.props.onClick && e.button === 0 && !this.props.isSelected(false)) {
e.preventDefault();
}
- if (e.button === 0 && this.props.isSelected(true) && !e.altKey && !e.ctrlKey && !e.metaKey) {
+ if (e.button === 0 && (this.props.rootSelected(true) || this.props.isSelected(true)) && !e.altKey && !e.ctrlKey && !e.metaKey) {
if (e.clientX < this.ProseRef!.getBoundingClientRect().right) { // stop propagation if not in sidebar
e.stopPropagation(); // if the text box is selected, then it consumes all down events
}
@@ -1248,7 +1247,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
FormattedTextBoxComment.textBox = this;
const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
!this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0))));
- FormattedTextBoxComment.update(editor, undefined, (e.target as any)?.className === "prosemirror-dropdownlink" ? (e.target as any).href : "");
+ const target = (e.target as any).parentElement; // hrefs are store don the database of the <a> node that wraps the hyerlink <span>
+ FormattedTextBoxComment.update(editor, undefined, target?.dataset?.targethrefs);
}
(e.nativeEvent as any).formattedHandled = true;
@@ -1276,10 +1276,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
FormattedTextBoxComment.Hide();
if (FormattedTextBoxComment.linkDoc) {
if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
- this.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight");
+ this.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "add" : "add:right");
} else {
DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, this.props.Document,
- (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+ (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation));
}
}
@@ -1316,7 +1316,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
onPointerWheel = (e: React.WheelEvent): void => {
// if a text note is not selected and scrollable, this prevents us from being able to scroll and zoom out at the same time
- if (this.props.isSelected(true) || e.currentTarget.scrollHeight > e.currentTarget.clientHeight) {
+ if ((this.props.rootSelected(true) || this.props.isSelected(true)) || e.currentTarget.scrollHeight > e.currentTarget.clientHeight) {
e.stopPropagation();
}
}
@@ -1339,7 +1339,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
if (!node && this.ProseRef) {
const lastNode = this.ProseRef.children[this.ProseRef.children.length - 1].children[this.ProseRef.children[this.ProseRef.children.length - 1].children.length - 1]; // get the last prosemirror div
- if (e.clientY > lastNode?.getBoundingClientRect().bottom) { // if we clicked below the last prosemirror div, then set the selection to be the end of the document
+ const boundsRect = lastNode?.getBoundingClientRect();
+ if (e.clientX > boundsRect.left && e.clientX < boundsRect.right &&
+ e.clientY > boundsRect.bottom) { // if we clicked below the last prosemirror div, then set the selection to be the end of the document
+ this._editorView?.focus();
this._editorView!.dispatch(this._editorView!.state.tr.setSelection(TextSelection.create(this._editorView!.state.doc, this._editorView!.state.doc.content.size)));
}
} else if ([this._editorView!.state.schema.nodes.ordered_list, this._editorView!.state.schema.nodes.listItem].includes(node?.type) &&
@@ -1415,14 +1418,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const self = this;
return new Plugin({
view(newView) {
- self.props.isSelected(true) && (RichTextMenu.Instance.view = newView);
+ self.props.isSelected(true) && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView);
return self.menuPlugin = new RichTextMenuPlugin({ editorProps: this.props });
}
});
}
public startUndoTypingBatch() {
- this._undoTyping = UndoManager.StartBatch("undoTyping");
+ !this._undoTyping && (this._undoTyping = UndoManager.StartBatch("undoTyping"));
}
public endUndoTypingBatch() {
@@ -1433,19 +1436,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
return wasUndoing;
}
+ public static LiveTextUndo: UndoManager.Batch | undefined;
public static HadSelection: boolean = false;
onBlur = (e: any) => {
FormattedTextBox.HadSelection = window.getSelection()?.toString() !== "";
- //DictationManager.Controls.stop(false);
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;
- onKeyPress = (e: React.KeyboardEvent) => {
+ onKeyDown = (e: React.KeyboardEvent) => {
+ // single line text boxes need to pass through tab/enter/backspace so that their containers can respond (eg, an outline container)
+ if (this.rootDoc._singleLine && ((e.key === "Backspace" && !this.dataDoc[this.fieldKey]?.Text) || ["Tab", "Enter"].includes(e.key))) {
+ return;
+ }
if (e.altKey) {
e.preventDefault();
return;
@@ -1478,9 +1487,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(mark));
}
- if (!this._undoTyping) {
- this.startUndoTypingBatch();
- }
+ this.startUndoTypingBatch();
}
ondrop = (eve: React.DragEvent) => {
@@ -1492,8 +1499,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
@action
tryUpdateHeight(limitHeight?: number) {
- let scrollHeight = this._ref.current?.scrollHeight;
- if (this.props.renderDepth && this.layoutDoc._autoHeight && !this.props.ignoreAutoHeight && scrollHeight) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
+ let scrollHeight = this.ProseRef?.scrollHeight || 0;
+ this.rootDoc[this.fieldKey + "-height"] = 0;
+ if (this.props.renderDepth && this.layoutDoc._autoHeight && !this.props.ignoreAutoHeight && scrollHeight && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
scrollHeight = scrollHeight * NumCast(this.layoutDoc._viewScale, 1);
if (limitHeight && scrollHeight > limitHeight) {
scrollHeight = limitHeight;
@@ -1503,7 +1511,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const nh = this.layoutDoc.isTemplateForField ? 0 : NumCast(this.layoutDoc._nativeHeight, 0);
const dh = NumCast(this.rootDoc._height, 0);
const newHeight = Math.max(10, (nh ? dh / nh * scrollHeight : scrollHeight) + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0));
- if (this.rootDoc !== this.layoutDoc.doc && !this.layoutDoc.resolvedDataDoc) {
+ if (!this.props.LayoutTemplateString && this.rootDoc !== this.layoutDoc.doc && !this.layoutDoc.resolvedDataDoc) {
// if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived...
console.log("Delayed height adjustment...");
setTimeout(() => {
@@ -1511,52 +1519,115 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.layoutDoc._nativeHeight = nh ? scrollHeight : undefined;
}, 10);
} else {
- this.layoutDoc._height = newHeight;
- this.layoutDoc._nativeHeight = nh ? scrollHeight : undefined;
+ try {
+ const boxHeight = Number(getComputedStyle(this._boxRef.current!).height.replace("px", ""));
+ const outer = this.rootDoc[HeightSym]() - boxHeight - (this.props.ChromeHeight ? this.props.ChromeHeight() : 0);
+ const finalHeight = newHeight + Math.max(0, outer);
+ const maxsidebar = !this.sidebarWidth() ? 0 : Array.from(this._boxRef.current!.getElementsByClassName("collectionStackingViewFieldColumn")).reduce((prev, ele) => Math.max(prev, Number(getComputedStyle(ele).height.replace("px", ""))), 0);
+ if (this.rootDoc._height !== finalHeight && finalHeight > maxsidebar) {
+ this.rootDoc._height = finalHeight;
+ this.layoutDoc._nativeHeight = nh ? scrollHeight : undefined;
+ }
+ this.rootDoc[this.fieldKey + "-height"] = finalHeight;
+ } catch (e) { console.log("Error in tryUpdateHeight"); }
}
}
}
+ @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?.title).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}
+ onClick={e => {
+ console.log(e);
+ }}
+ />;
+ }
+
+ @computed get sidebarCollection() {
+ const fitToBox = this.props.Document._fitToBox;
+ const collectionProps = {
+ ...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit,
+ 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: 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,
+ };
+ 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")); }
+ @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;
- this.props.isSelected() && setTimeout(() => this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props), 0); // need to make sure that we update a text box that is selected after updating the one that was deselected
- if (!this.props.isSelected() && FormattedTextBoxComment.textBox === this) {
- setTimeout(() => FormattedTextBoxComment.Hide(), 0);
- }
- const selPad = this.props.isSelected() ? -10 : 0;
- const selclass = this.props.isSelected() ? "-selected" : "";
+ const interactive = (Doc.GetSelectedTool() === InkTool.None || SnappingManager.GetIsDragging()) && !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 minimal = this.props.ignoreAutoHeight;
+ 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"}
+ <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)`,
- ...this.styleFromLayoutString(scale)
+ ...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}
style={{
overflow: this.layoutDoc._autoHeight ? "hidden" : undefined,
width: "100%",
- height: this.props.height ? this.props.height : this.layoutDoc._autoHeight && this.props.renderDepth ? "max-content" : undefined,
+ height: this.props.height || (this.layoutDoc._autoHeight && this.props.renderDepth ? "max-content" : undefined),
background: Doc.UserDoc().renderStyle === "comic" ? "transparent" : this.props.background ? this.props.background : StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : ""),
- opacity: this.props.hideOnLeave ? (this._entered ? 1 : 0.1) : 1,
color: this.props.color ? this.props.color : StrCast(this.layoutDoc[this.props.fieldKey + "-color"], this.props.hideOnLeave ? "white" : "inherit"),
pointerEvents: interactive ? undefined : "none",
- fontSize: Cast(this.layoutDoc._fontSize, "string", null),
+ fontSize: this.props.fontSize || Cast(this.layoutDoc._fontSize, "string", null),
fontWeight: Cast(this.layoutDoc._fontWeight, "number", null),
fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit"),
- transition: "opacity 1s"
}}
onContextMenu={this.specificContextMenu}
- onKeyDown={this.onKeyPress}
+ onKeyDown={this.onKeyDown}
onFocus={this.onFocused}
onClick={this.onClick}
onPointerMove={e => this.hitBulletTargets(e.clientX, e.clientY, e.shiftKey, true)}
@@ -1566,66 +1637,29 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
onMouseUp={this.onMouseUp}
onWheel={this.onPointerWheel}
onPointerEnter={action(() => this._entered = true)}
- onPointerLeave={action((e: React.PointerEvent<HTMLDivElement>) => {
+ onPointerLeave={action(e => {
this._entered = false;
const target = document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y);
for (let child: any = target; child; child = child?.parentElement) {
- if (child === this._ref.current!) {
- this._entered = true;
- }
+ child === this._ref.current! && (this._entered = true);
}
})}
onDoubleClick={this.onDoubleClick}
>
<div className={`formattedTextBox-outer`} ref={this._scrollRef}
- style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: !this.props.active() ? "none" : undefined }}
+ style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: !active && !SnappingManager.GetIsDragging() ? "none" : undefined }}
onScroll={this.onscrolled} onDrop={this.ondrop} >
- <div className={`formattedTextBox-inner${rounded}${selclass}`} ref={this.createDropTarget}
+ <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: !this.props.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 ? (null) : this.sidebarWidthPercent === "0%" ?
- <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} /> :
- <div className={"formattedTextBox-sidebar" + (Doc.GetSelectedTool() !== InkTool.None ? "-inking" : "")}
- style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
- <CollectionFreeFormView {...this.props}
- PanelHeight={this.props.PanelHeight}
- PanelWidth={this.sidebarWidth}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
- scaleField={this.annotationKey + "-scale"}
- annotationsKey={this.annotationKey}
- isAnnotationOverlay={false}
- 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}>
- </CollectionFreeFormView>
- <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} />
- </div>}
- {!this.layoutDoc._showAudio ? (null) :
- <div className="formattedTextBox-dictation"
- onPointerDown={e => {
- runInAction(() => this._recording = !this._recording);
- setTimeout(() => this._editorView!.focus(), 500);
- e.stopPropagation();
- }} >
- <FontAwesomeIcon className="formattedTextBox-audioFont"
- style={{ color: this._recording ? "red" : "blue", opacity: this._recording ? 1 : 0.5, display: this.props.isSelected() ? "" : "none" }} 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..582ada6ce 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;
@@ -61,7 +62,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 6f3984f39..5183e193b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -1,28 +1,28 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Tooltip } from "@material-ui/core";
+import { action, observable } from "mobx";
import { Mark, ResolvedPos } from "prosemirror-model";
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 { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath, returnZero, returnOne, returnEmptyFilter } from "../../../../Utils";
+import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, Utils } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
-import { DocumentManager } from "../../../util/DocumentManager";
-import { schema } from "./schema_rts";
+import { Docs } from "../../../documents/Documents";
+import { DocumentType } from "../../../documents/DocumentTypes";
+import { LinkManager } from "../../../util/LinkManager";
import { Transform } from "../../../util/Transform";
+import { undoBatch } from "../../../util/UndoManager";
import { ContentFittingDocumentView } from "../ContentFittingDocumentView";
+import { DocumentLinksButton } from "../DocumentLinksButton";
+import { DocumentView } from "../DocumentView";
+import { LinkDocPreview } from "../LinkDocPreview";
import { FormattedTextBox } from "./FormattedTextBox";
import './FormattedTextBoxComment.scss';
+import { schema } from "./schema_rts";
import React = require("react");
-import { Docs } from "../../../documents/Documents";
-import wiki from "wikijs";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action } from "mobx";
-import { LinkManager } from "../../../util/LinkManager";
-import { LinkDocPreview } from "../LinkDocPreview";
-import { DocumentLinksButton } from "../DocumentLinksButton";
-import { Tooltip } from "@material-ui/core";
-import { undoBatch } from "../../../util/UndoManager";
export let formattedTextBoxCommentPlugin = new Plugin({
view(editorView) { return new FormattedTextBoxComment(editorView); }
@@ -60,6 +60,7 @@ export function findEndOfMark(rpos: ResolvedPos, view: EditorView, finder: (mark
// this view appears when clicking on text that has a hyperlink which is configured to show a preview of its target.
// this will also display metadata information about text when the view is configured to display things like other people who authored text.
//
+
export class FormattedTextBoxComment {
static tooltip: HTMLElement;
static tooltipText: HTMLElement;
@@ -72,6 +73,13 @@ export class FormattedTextBoxComment {
static _deleteRef: Opt<HTMLDivElement | null>;
static _followRef: Opt<HTMLDivElement | null>;
+ static _nextRef: Opt<HTMLDivElement | null>;
+
+ static _lastState?: EditorState;
+ static _lastView?: EditorView;
+
+ @observable static _hrefInd = 0;
+ static _hrefs: string[] | undefined = [];
constructor(view: any) {
if (!FormattedTextBoxComment.tooltip) {
@@ -80,84 +88,67 @@ 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);
FormattedTextBoxComment.tooltip.className = "FormattedTextBox-tooltip";
FormattedTextBoxComment.tooltip.style.pointerEvents = "all";
- FormattedTextBoxComment.tooltip.style.maxWidth = "200px";
+ 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";
- FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipInput);
+ // FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipInput);
FormattedTextBoxComment.tooltip.onpointerdown = async (e: PointerEvent) => {
const keep = e.target && (e.target as any).type === "checkbox" ? true : false;
const textBox = FormattedTextBoxComment.textBox;
- if (FormattedTextBoxComment.linkDoc && !keep && textBox) {
- if (FormattedTextBoxComment.linkDoc.author) {
-
- if (FormattedTextBoxComment._deleteRef && FormattedTextBoxComment._deleteRef.contains(e.target as any)) {
+ const linkDoc = FormattedTextBoxComment.linkDoc;
+ if (linkDoc && !keep && textBox) {
+ if (linkDoc.author) {
+ if (FormattedTextBoxComment._deleteRef?.contains(e.target as any)) {
this.deleteLink();
- } else if (FormattedTextBoxComment._followRef && FormattedTextBoxComment._followRef.contains(e.target as any)) {
- if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
- textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight");
- } else {
- const anchor = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(FormattedTextBoxComment.linkDoc.anchor1, Doc)), textBox.dataDoc) ?
- Cast(FormattedTextBoxComment.linkDoc.anchor2, Doc) : (Cast(FormattedTextBoxComment.linkDoc.anchor1, Doc))
- || FormattedTextBoxComment.linkDoc);
- const target = anchor?.annotationOn ? await DocCastAsync(anchor.annotationOn) : anchor;
-
- if (FormattedTextBoxComment.linkDoc.follow) {
- if (FormattedTextBoxComment.linkDoc.follow === "Default") {
- DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "onRight"), false);
- } else if (FormattedTextBoxComment.linkDoc.follow === "Always open in right tab") {
- if (target) { textBox.props.addDocTab(target, "onRight"); }
- } else if (FormattedTextBoxComment.linkDoc.follow === "Always open in new tab") {
- if (target) { textBox.props.addDocTab(target, "inTab"); }
- }
- } else {
- DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "onRight"), false);
- }
- }
+ } else if (FormattedTextBoxComment._nextRef?.contains(e.target as any)) {
+ FormattedTextBoxComment.showPreview(FormattedTextBoxComment._lastView!, FormattedTextBoxComment._lastState, FormattedTextBoxComment._hrefs?.[(++FormattedTextBoxComment._hrefInd) % FormattedTextBoxComment._hrefs?.length]);
} else {
- if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
- textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight");
+ FormattedTextBoxComment.linkDoc = undefined;
+ if (linkDoc.type !== DocumentType.LINK) {
+ textBox.props.addDocTab(linkDoc, e.ctrlKey ? "add" : "add:right");
} else {
- DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
- (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+ const target = LinkManager.getOppositeAnchor(linkDoc, textBox.dataDoc);
+ target && DocumentView.followLinkClick(linkDoc, textBox.dataDoc, textBox.props, e.shiftKey, e.altKey);
}
}
-
}
} else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) {
- textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400, UseCors: true }), "onRight");
+ textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _fitWidth: true, _width: 200, _height: 400, useCors: true }), "add:right");
}
keep && textBox && FormattedTextBoxComment.start !== undefined && textBox.adoptAnnotation(
FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark);
e.stopPropagation();
e.preventDefault();
};
- root && root.appendChild(FormattedTextBoxComment.tooltip);
+ root?.appendChild(FormattedTextBoxComment.tooltip);
}
}
@undoBatch
- @action
- deleteLink = () => {
+ deleteLink = action(() => {
FormattedTextBoxComment.linkDoc ? LinkManager.Instance.deleteLink(FormattedTextBoxComment.linkDoc) : null;
LinkDocPreview.LinkInfo = undefined;
DocumentLinksButton.EditLink = undefined;
- //FormattedTextBoxComment.tooltipText = undefined;
FormattedTextBoxComment.Hide();
- }
+ });
public static Hide() {
FormattedTextBoxComment.textBox = undefined;
+ FormattedTextBoxComment.linkDoc = undefined;
FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none");
- ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
+ try {
+ ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
+ FormattedTextBoxComment.tooltip.removeChild(FormattedTextBoxComment.tooltipText);
+ } catch (e) { }
}
public static SetState(textBox: any, start: number, end: number, mark: Mark) {
FormattedTextBoxComment.textBox = textBox;
@@ -185,14 +176,20 @@ export class FormattedTextBoxComment {
}
static update(view: EditorView, lastState?: EditorState, forceUrl: string = "") {
- const state = view.state;
// Don't do anything if the document/selection didn't change
- if (lastState && lastState.doc.eq(state.doc) &&
- lastState.selection.eq(state.selection)) {
+ if (!forceUrl && lastState?.doc.eq(view.state.doc) && lastState?.selection.eq(view.state.selection)) {
return;
}
+ FormattedTextBoxComment._lastState = lastState;
+ FormattedTextBoxComment._lastView = view;
+ FormattedTextBoxComment._hrefs = forceUrl ? forceUrl.trim().split(" ") : undefined;
+ FormattedTextBoxComment._hrefInd = 0;
FormattedTextBoxComment.linkDoc = undefined;
+ FormattedTextBoxComment.showPreview(view, lastState, FormattedTextBoxComment._hrefs?.[FormattedTextBoxComment._hrefInd]);
+ }
+ static showPreview(view: EditorView, lastState?: EditorState, forceUrl: string = "") {
+ const state = view.state;
const textBox = FormattedTextBoxComment.textBox;
if (!textBox || !textBox.props) {
return;
@@ -217,7 +214,7 @@ export class FormattedTextBoxComment {
FormattedTextBoxComment.SetState(FormattedTextBoxComment.textBox, state.selection.$from.pos - nbef, state.selection.$from.pos + naft, mark);
}
if (mark && child && ((nbef && naft) || !noselection)) {
- FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " date=" + (new Date(mark.attrs.modified * 5000)).toDateString();
+ FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " on " + (new Date(mark.attrs.modified * 1000)).toLocaleString();
set = "";
FormattedTextBoxComment.tooltipInput.style.display = "";
}
@@ -230,10 +227,22 @@ export class FormattedTextBoxComment {
state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node));
child = child || (nbef && state.selection.$from.nodeBefore);
const mark = child ? findLinkMark(child.marks) : undefined;
- const href = (!mark?.attrs.docref || naft === nbef) && mark?.attrs.allLinks.find((item: { href: string }) => item.href)?.href || forceUrl;
+ const href = forceUrl || (!mark?.attrs.docref || naft === nbef) && mark?.attrs.allLinks.find((item: { href: string }) => item.href)?.href;
if (forceUrl || (href && child && nbef && naft && mark?.attrs.showPreview)) {
- FormattedTextBoxComment.tooltipText.textContent = "external => " + href;
+ try {
+ ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
+ 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";
+ FormattedTextBoxComment.tooltipText.style.cursor = "pointer";
+ FormattedTextBoxComment.tooltipText.textContent = "URL: " + href;
(FormattedTextBoxComment.tooltipText as any).href = href;
+ FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText);
+
if (href.startsWith("https://en.wikipedia.org/wiki/")) {
wiki().page(href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(summary => FormattedTextBoxComment.tooltipText.textContent = summary.substring(0, 500)));
} else {
@@ -241,12 +250,9 @@ export class FormattedTextBoxComment {
FormattedTextBoxComment.tooltipText.style.overflow = "hidden";
}
if (href.indexOf(Utils.prepend("/doc/")) === 0) {
+ const docTarget = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
FormattedTextBoxComment.tooltipText.textContent = "target not found...";
(FormattedTextBoxComment.tooltipText as any).href = "";
- const docTarget = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
- try {
- ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
- } catch (e) { }
docTarget && DocServer.GetRefField(docTarget).then(async linkDoc => {
if (linkDoc instanceof Doc) {
(FormattedTextBoxComment.tooltipText as any).href = href;
@@ -259,34 +265,35 @@ export class FormattedTextBoxComment {
if (target?.author) {
FormattedTextBoxComment.showCommentbox("", view, nbef);
- const title = StrCast(target.title).length > 16 ?
- StrCast(target.title).substr(0, 16) + "..." : target.title;
-
+ const title = StrCast(target.title).length > 16 ? StrCast(target.title).substr(0, 16) + "..." : target.title;
const docPreview = <div className="FormattedTextBoxComment">
<div className="FormattedTextBoxComment-info">
<div className="FormattedTextBoxComment-title">
{title}
- {FormattedTextBoxComment.linkDoc.description !== "" ? <p className="FormattedTextBoxComment-description">
- {StrCast(FormattedTextBoxComment.linkDoc.description)}</p> : null}
+ {FormattedTextBoxComment.linkDoc.description === "" ? (null) :
+ <p className="FormattedTextBoxComment-description"> {StrCast(FormattedTextBoxComment.linkDoc.description)}</p>}
</div>
<div className="wrapper" style={{ float: "right" }}>
+ {(FormattedTextBoxComment._hrefs?.length || 0) <= 1 ? (null) : <Tooltip title={<><div className="dash-tooltip">Next Link</div></>} placement="top">
+ <div className="FormattedTextBoxComment-button" ref={(r) => this._nextRef = r}>
+ <FontAwesomeIcon className="FormattedTextBoxComment-fa-icon" icon="chevron-right" color="white" size="sm" />
+ </div>
+ </Tooltip>}
<Tooltip title={<><div className="dash-tooltip">Delete Link</div></>} placement="top">
- <div className="FormattedTextBoxComment-button"
- ref={(r) => this._deleteRef = r}>
- <FontAwesomeIcon className="FormattedTextBoxComment-fa-icon" icon="trash" color="white"
- size="sm" /></div>
+ <div className="FormattedTextBoxComment-button" ref={(r) => this._deleteRef = r}>
+ <FontAwesomeIcon className="FormattedTextBoxComment-fa-icon" icon="trash" color="white" size="sm" />
+ </div>
</Tooltip>
<Tooltip title={<><div className="dash-tooltip">Follow Link</div></>} placement="top">
- <div className="FormattedTextBoxComment-button"
- ref={(r) => this._followRef = r}>
- <FontAwesomeIcon className="FormattedTextBoxComment-fa-icon" icon="arrow-right" color="white"
- size="sm" />
+ <div className="FormattedTextBoxComment-button" ref={(r) => this._followRef = r}>
+ <FontAwesomeIcon className="FormattedTextBoxComment-fa-icon" icon="arrow-right" color="white" size="sm" />
</div>
</Tooltip>
- </div> </div>
+ </div>
+ </div>
<div className="FormattedTextBoxComment-preview-wrapper">
<ContentFittingDocumentView
Document={target}
@@ -302,29 +309,29 @@ export class FormattedTextBoxComment {
pinToPres={returnFalse}
dontRegisterView={true}
docFilters={returnEmptyFilter}
+ docRangeFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionDoc={undefined}
ContainingCollectionView={undefined}
- renderDepth={0}
+ renderDepth={-1}
PanelWidth={() => 175} //Math.min(350, NumCast(target._width, 350))}
PanelHeight={() => 175} //Math.min(250, NumCast(target._height, 250))}
focus={emptyFunction}
whenActiveChanged={returnFalse}
bringToFront={returnFalse}
ContentScaling={returnOne}
- NativeWidth={() => target._nativeWidth ? NumCast(target._nativeWidth) : 0}
- NativeHeight={() => target._nativeHeight ? NumCast(target._nativeHeight) : 0}
+ NativeWidth={target._nativeWidth ? (() => NumCast(target._nativeWidth)) : undefined}
+ NativeHeight={target._natvieHeight ? (() => NumCast(target._nativeHeight)) : undefined}
/>
</div>
</div>;
-
-
FormattedTextBoxComment.showCommentbox("", view, nbef);
ReactDOM.render(docPreview, FormattedTextBoxComment.tooltipText);
- FormattedTextBoxComment.tooltip.style.width = NumCast(target._width) ? `${NumCast(target._width)}` : "100%";
- FormattedTextBoxComment.tooltip.style.height = NumCast(target._height) ? `${NumCast(target._height)}` : "100%";
+ //FormattedTextBoxComment.tooltip.style.width = "100%";
+ FormattedTextBoxComment.tooltip.style.height = "100%";
}
}
});
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 8faf752b4..cb5823e86 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -7,11 +7,13 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list";
import { EditorState, Transaction, TextSelection } from "prosemirror-state";
import { SelectionManager } from "../../../util/SelectionManager";
import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types";
-import { Doc, DataSym } from "../../../../fields/Doc";
+import { Doc, DataSym, DocListCast } from "../../../../fields/Doc";
import { FormattedTextBox } from "./FormattedTextBox";
import { Id } from "../../../../fields/FieldSymbols";
import { Docs } from "../../../documents/Documents";
import { Utils } from "../../../../Utils";
+import { listSpec } from "../../../../fields/Schema";
+import { List } from "../../../../fields/List";
const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
@@ -33,7 +35,7 @@ export let updateBullets = (tx2: Transaction, schema: Schema, assignedMapStyle?:
return tx2;
};
-export default function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKeys?: KeyMap): KeyMap {
+export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKeys?: KeyMap): KeyMap {
const keys: { [key: string]: any } = {};
function bind(key: string, cmd: any) {
@@ -45,6 +47,29 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
keys[key] = cmd;
}
+ /// bcz; Argh!! replace with an onEnter func that conditionally handles Enter
+ const addTextBox = (below: boolean, force?: boolean) => {
+ if (props.Document.treeViewOutlineMode) return true; // bcz: Arghh .. need to determine if this is an treeViewOutlineBox in which case Enter's are ignored..
+ const layoutDoc = props.Document;
+ const originalDoc = layoutDoc.rootDocument || layoutDoc;
+ if (force || props.Document._singleLine) {
+ const layoutKey = StrCast(originalDoc.layoutKey);
+ const newDoc = Doc.MakeCopy(originalDoc, true);
+ const dataField = originalDoc[Doc.LayoutFieldKey(newDoc)];
+ newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
+ if (below) newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10;
+ else newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10;
+ if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
+ newDoc[layoutKey] = originalDoc[layoutKey];
+ }
+ Doc.GetProto(newDoc).text = undefined;
+ FormattedTextBox.SelectOnLoad = newDoc[Id];
+ props.addDocument(newDoc);
+ return true;
+ }
+ return false;
+ };
+
//History commands
bind("Mod-z", undo);
bind("Shift-Mod-z", redo);
@@ -66,6 +91,11 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
bind("Ctrl-i", wrapInList(schema.nodes.ordered_list));
bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
+ /// bcz; Argh!! replace layotuTEmpalteString with a onTab prop conditionally handles Tab);
+ if (props.Document._singleLine) {
+ if (!props.LayoutTemplateString) return addTextBox(false, true);
+ return true;
+ }
const ref = state.selection;
const range = ref.$from.blockRange(ref.$to);
const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
@@ -89,6 +119,8 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
});
bind("Shift-Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
+ /// bcz; Argh!! replace with a onShiftTab prop conditionally handles Tab);
+ if (props.Document._singleLine) return true;
const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
if (!liftListItem(schema.nodes.list_item)(state.tr, (tx2: Transaction) => {
@@ -104,7 +136,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
//Command to create a new Tab with a PDF of all the command shortcuts
bind("Mod-/", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
const newDoc = Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _fitWidth: true, _width: 300, _height: 300 });
- props.addDocTab(newDoc, "onRight");
+ props.addDocTab(newDoc, "add:right");
});
//Commands to modify BlockType
@@ -136,47 +168,11 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
return tx;
};
- const addTextOnRight = (force: boolean) => {
- const layoutDoc = props.Document;
- const originalDoc = layoutDoc.rootDocument || layoutDoc;
- if (force || props.Document._singleLine) {
- const layoutKey = StrCast(originalDoc.layoutKey);
- const newDoc = Doc.MakeCopy(originalDoc, true);
- newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined;
- newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10;
- if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
- newDoc[layoutKey] = originalDoc[layoutKey];
- }
- Doc.GetProto(newDoc).text = undefined;
- FormattedTextBox.SelectOnLoad = newDoc[Id];
- props.addDocument(newDoc);
- return true;
- }
- return false;
- };
-
//Command to create a text document to the right of the selected textbox
- bind("Alt-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
- return addTextOnRight(true);
- });
+ bind("Alt-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => addTextBox(false, true));
//Command to create a text document to the bottom of the selected textbox
- bind("Ctrl-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
- const layoutDoc = props.Document;
- const originalDoc = layoutDoc.rootDocument || layoutDoc;
- if (originalDoc instanceof Doc) {
- const layoutKey = StrCast(originalDoc.layoutKey);
- const newDoc = Doc.MakeCopy(originalDoc, true);
- newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined;
- newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10;
- if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
- newDoc[layoutKey] = originalDoc[layoutKey];
- }
- Doc.GetProto(newDoc).text = undefined;
- FormattedTextBox.SelectOnLoad = newDoc[Id];
- props.addDocument(newDoc);
- }
- });
+ bind("Ctrl-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => addTextBox(true, true));
// backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward);
bind("Backspace", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
@@ -199,7 +195,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
//newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock
//command to break line
bind("Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
- if (addTextOnRight(false)) return true;
+ if (addTextBox(true, 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;
@@ -221,11 +217,13 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
const fromattrs = state.selection.$from.node().attrs;
if (!splitBlockKeepMarks(state, (tx3: Transaction) => {
const tonode = tx3.selection.$to.node();
- const tx4 = tx3.setNodeMarkup(tx3.selection.to - 1, tonode.type, fromattrs, tonode.marks);
- splitMetadata(marks, tx4);
- if (!liftListItem(schema.nodes.list_item)(tx4, dispatch as ((tx: Transaction<Schema<any, any>>) => void))) {
- dispatch(tx4);
- }
+ if (tx3.selection.to && tx3.doc.nodeAt(tx3.selection.to - 1)) {
+ const tx4 = tx3.setNodeMarkup(tx3.selection.to - 1, tonode.type, fromattrs, tonode.marks);
+ splitMetadata(marks, tx4);
+ if (!liftListItem(schema.nodes.list_item)(tx4, dispatch as ((tx: Transaction<Schema<any, any>>) => void))) {
+ dispatch(tx4);
+ }
+ } else dispatch(tx3.insertText("\r\n"));
})) {
return false;
}
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 213b341e8..68239a8f1 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -1,8 +1,8 @@
import React = require("react");
-import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faBold, faCaretDown, faChevronLeft, faEyeDropper, faHighlighter, faOutdent, faIndent, faHandPointLeft, faHandPointRight, faItalic, faLink, faPaintRoller, faPalette, faStrikethrough, faSubscript, faSuperscript, faUnderline } from "@fortawesome/free-solid-svg-icons";
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable, IReactionDisposer, reaction } from "mobx";
+import { Tooltip } from "@material-ui/core";
+import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import { lift, wrapIn } from "prosemirror-commands";
import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos } from "prosemirror-model";
@@ -11,27 +11,24 @@ import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { Doc } from "../../../../fields/Doc";
import { DarkPastelSchemaPalette, PastelSchemaPalette } from '../../../../fields/SchemaHeaderField';
-import { Cast, StrCast, BoolCast, NumCast } from "../../../../fields/Types";
+import { Cast, StrCast } from "../../../../fields/Types";
+import { TraceMobx } from "../../../../fields/util";
import { unimplementedFunction, Utils } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { LinkManager } from "../../../util/LinkManager";
import { SelectionManager } from "../../../util/SelectionManager";
-import AntimodeMenu, { AntimodeMenuProps } from "../../AntimodeMenu";
+import { undoBatch, UndoManager } from "../../../util/UndoManager";
+import { AntimodeMenu, AntimodeMenuProps } 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";
-import { UndoManager, undoBatch } from "../../../util/UndoManager";
-import { Tooltip } from "@material-ui/core";
const { toggleMark } = require("prosemirror-commands");
-library.add(faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faOutdent, faIndent, faHandPointLeft, faHandPointRight, faEyeDropper, faCaretDown, faPalette, faHighlighter, faLink, faPaintRoller);
-
@observer
-export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
+export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
static Instance: RichTextMenu;
public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable
@@ -78,24 +75,24 @@ export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
RichTextMenu.Instance = this;
this._canFade = false;
//this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]);
- this.Pinned = true;
+ runInAction(() => this.Pinned = true);
this.fontSizeOptions = [
- { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 8 }), title: "Set font size", label: "8pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 9 }), title: "Set font size", label: "9pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 10 }), title: "Set font size", label: "10pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 12 }), title: "Set font size", label: "12pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 14 }), title: "Set font size", label: "14pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 16 }), title: "Set font size", label: "16pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 18 }), title: "Set font size", label: "18pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 20 }), title: "Set font size", label: "20pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 24 }), title: "Set font size", label: "24pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 32 }), title: "Set font size", label: "32pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 48 }), title: "Set font size", label: "48pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 72 }), title: "Set font size", label: "72pt", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 8 }), title: "Set font size", label: "8px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 9 }), title: "Set font size", label: "9px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 10 }), title: "Set font size", label: "10px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 12 }), title: "Set font size", label: "12px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 14 }), title: "Set font size", label: "14px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 16 }), title: "Set font size", label: "16px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 18 }), title: "Set font size", label: "18px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 20 }), title: "Set font size", label: "20px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 24 }), title: "Set font size", label: "24px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 32 }), title: "Set font size", label: "32px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 48 }), title: "Set font size", label: "48px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 72 }), title: "Set font size", label: "72px", command: this.changeFontSize },
{ mark: null, title: "", label: "...", command: unimplementedFunction, hidden: true },
- { mark: null, title: "", label: "13pt", command: unimplementedFunction, hidden: true }, // this is here because the default size is 13, but there is no actual 13pt option
+ { mark: null, title: "", label: "13px", command: unimplementedFunction, hidden: true }, // this is here because the default size is 13, but there is no actual 13pt option
];
this.fontFamilyOptions = [
@@ -180,7 +177,7 @@ export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this.activeListType = this.getActiveListStyle();
this.activeAlignment = this.getActiveAlignment();
this.activeFontFamily = !activeFamilies.length ? "Arial" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various";
- this.activeFontSize = !activeSizes.length ? "13pt" : activeSizes.length === 1 ? String(activeSizes[0]) : "...";
+ this.activeFontSize = !activeSizes.length ? "13px" : activeSizes.length === 1 ? String(activeSizes[0]) : "...";
this.activeFontColor = !activeColors.length ? "black" : activeColors.length === 1 ? String(activeColors[0]) : "...";
this.activeHighlightColor = !activeHighlights.length ? "" : activeHighlights.length === 1 ? String(activeHighlights[0]) : "...";
@@ -258,7 +255,7 @@ export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
marks.forEach(m => {
m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family);
m.type === state.schema.marks.pFontColor && activeColors.push(m.attrs.color);
- m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "pt");
+ m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "px");
m.type === state.schema.marks.marker && activeHighlights.push(String(m.attrs.highlight));
});
}
@@ -387,7 +384,7 @@ export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
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() + "pt"); break;
+ case schema.marks.pFontSize: setter(Doc.UserDoc().fontSize = mark.attrs.fontSize.toString() + "px"); break;
}
}
else UndoManager.RunInBatch(() => self.view && mark && command(mark, self.view), "text mark dropdown");
@@ -428,7 +425,7 @@ export default 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;
+ 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);
@@ -678,6 +675,7 @@ export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@action toggleColorDropdown() { this.showColorDropdown = !this.showColorDropdown; }
@action setActiveColor(color: string) { this.activeFontColor = color; }
get TextView() { return (this.view as any)?.TextView as FormattedTextBox; }
+ get TextViewFieldKey() { return this.TextView?.props.fieldKey; }
createColorButton() {
const self = this;
@@ -813,7 +811,7 @@ export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
<div className="dropdown link-menu">
<p>Linked to:</p>
<input value={link} placeholder="Enter URL" onChange={onLinkChange} />
- <button className="make-button" onPointerDown={e => this.makeLinkToURL(link, "onRight")}>Apply hyperlink</button>
+ <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>
</div>;
@@ -860,7 +858,7 @@ export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
// TODO: should check for valid URL
@undoBatch
makeLinkToURL = (target: string, lcoation: string) => {
- ((this.view as any)?.TextView as FormattedTextBox).makeLinkToSelection("", target, "onRight", "", target);
+ ((this.view as any)?.TextView as FormattedTextBox).makeLinkToSelection("", target, "onRadd:rightight", "", target);
}
@undoBatch
@@ -1021,6 +1019,7 @@ interface ButtonDropdownProps {
dropdownContent: JSX.Element;
openDropdownOnButton?: boolean;
link?: boolean;
+ pdf?: boolean;
}
@observer
@@ -1060,13 +1059,22 @@ export class ButtonDropdown extends React.Component<ButtonDropdownProps> {
}, 0);
}
+
render() {
return (
<div className="button-dropdown-wrapper" ref={node => this.ref = node}>
- <div className="antimodeMenu-button dropdown-button-combined" onPointerDown={this.onDropdownClick}>
- {this.props.button}
- <div style={{ marginTop: "-8.5" }}><FontAwesomeIcon icon="caret-down" size="sm" /></div>
- </div>
+ {!this.props.pdf ?
+ <div className="antimodeMenu-button dropdown-button-combined" onPointerDown={this.onDropdownClick}>
+ {this.props.button}
+ <div style={{ marginTop: "-8.5" }}><FontAwesomeIcon icon="caret-down" size="sm" /></div>
+ </div>
+ :
+ <>
+ {this.props.button}
+ <button className="dropdown-button antimodeMenu-button" key="antimodebutton" onPointerDown={this.onDropdownClick}>
+ <FontAwesomeIcon icon="caret-down" size="sm" />
+ </button>
+ </>}
{this.showDropdown ? this.props.dropdownContent : (null)}
</div>
);
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index dc1d8a2c8..921c0e128 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -3,13 +3,13 @@ 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 } from "../../../../fields/Types";
+import { Cast, NumCast, StrCast } from "../../../../fields/Types";
import { returnFalse, Utils } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { Docs, DocUtils } from "../../../documents/Documents";
import { FormattedTextBox } from "./FormattedTextBox";
import { wrappingInputRule } from "./prosemirrorPatches";
-import RichTextMenu from "./RichTextMenu";
+import { RichTextMenu } from "./RichTextMenu";
import { schema } from "./schema_rts";
import { List } from "../../../../fields/List";
@@ -90,9 +90,9 @@ export class RichTextRules {
textDoc.inlineTextCount = numInlines + 1;
const inlineFieldKey = "inline" + numInlines; // which field on the text document this annotation will write to
const inlineLayoutKey = "layout_" + inlineFieldKey; // the field holding the layout string that will render the inline annotation
- const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: "9pt", title: "inline comment" });
+ const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: "9px", title: "inline comment" });
textDocInline.title = inlineFieldKey; // give the annotation its own title
- textDocInline.customTitle = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
+ textDocInline["title-custom"] = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
textDocInline.proto = textDoc; // make the annotation inherit from the outer text doc so that it can resolve any nested field references, e.g., [[field]]
textDocInline._textContext = ComputedField.MakeFunction(`copyField(self.${inlineFieldKey})`);
@@ -267,19 +267,21 @@ 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 docid = match[3]?.substring(1);
+ const rawdocid = match[3]?.substring(1);
+ const docid = rawdocid ? (!rawdocid.includes("@") ? Doc.CurrentUserEmail + "@" + rawdocid : rawdocid).replace(".", "_") : undefined;
const value = match[2]?.substring(1);
if (!fieldKey) {
+ const linkId = Utils.GenerateGuid();
if (docid) {
DocServer.GetRefField(docid).then(docx => {
- const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500, }, docid);
- DocUtils.Publish(target, docid, returnFalse, returnFalse);
- DocUtils.MakeLink({ doc: this.Document }, { doc: target }, "portal to");
+ const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: rawdocid, _width: 500, _height: 500, }, docid);
+ DocUtils.MakeLink({ doc: this.Document }, { doc: target }, "portal to", undefined, linkId);
});
- const link = state.schema.marks.linkAnchor.create({ href: Utils.prepend("/doc/" + docid), location: "onRight", title: docid, targetId: docid });
+ const allLinks = [{ href: Utils.prepend("/doc/" + docid), title: docid, targetId: docid, linkId }];
+ const link = state.schema.marks.linkAnchor.create({ allLinks, title: rawdocid, location: "add:right" });
return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 2).addMark(start, end - 3, link);
}
return state.tr;
@@ -297,15 +299,16 @@ export class RichTextRules {
// {{<layout>}} => show layout for this doc
// {{<layout> : Doc}} => show layout for another doc
new InputRule(
- new RegExp(/\{\{([a-zA-Z_ \-0-9]*)(\([a-zA-Z0-9…._/\-]*\))?(:[a-zA-Z_ \-0-9]+)?\}\}$/),
+ new RegExp(/\{\{([a-zA-Z_ \-0-9]*)(\([a-zA-Z0-9…._/\-]*\))?(:[a-zA-Z_@\.\? \-0-9]+)?\}\}$/),
(state, match, start, end) => {
const fieldKey = match[1] || "";
const fieldParam = match[2]?.replace("…", "...") || "";
- const docid = match[3]?.substring(1);
+ const rawdocid = match[3]?.substring(1);
+ const docid = rawdocid ? (!rawdocid.includes("@") ? Doc.CurrentUserEmail + "@" + rawdocid : rawdocid).replace(".", "_") : undefined;
if (!fieldKey && !docid) return state.tr;
docid && DocServer.GetRefField(docid).then(docx => {
if (!(docx instanceof Doc && docx)) {
- const docx = Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500 }, docid);
+ const docx = Docs.Create.FreeformDocument([], { title: rawdocid, _width: 500, _height: 500 }, docid);
DocUtils.Publish(docx, docid, returnFalse, returnFalse);
}
});
@@ -321,7 +324,11 @@ export class RichTextRules {
(state, match, start, end) => {
const tag = match[1];
if (!tag) return state.tr;
- this.Document[DataSym]["#" + tag] = ".";
+ this.Document[DataSym]["#" + tag] = "#" + tag;
+ const tags = StrCast(this.Document[DataSym].tags, ":");
+ if (!tags.includes(`#${tag}:`)) {
+ this.Document[DataSym].tags = `${tags + "#" + tag + ':'}`;
+ }
const fieldView = state.schema.nodes.dashField.create({ fieldKey: "#" + tag });
return state.tr.deleteRange(start, end).insert(start, fieldView);
}),
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index 33a080fe4..1767a04de 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -13,6 +13,7 @@ import { Transform } from "../../../util/Transform";
import { DocumentView } from "../DocumentView";
import { FormattedTextBox } from "./FormattedTextBox";
import React = require("react");
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
export class DashDocView {
@@ -43,7 +44,7 @@ export class DashDocView {
this._outer = document.createElement("span");
this._outer.style.position = "relative";
this._outer.style.textIndent = "0";
- this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+ this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray"));
this._outer.style.width = node.attrs.width;
this._outer.style.height = node.attrs.height;
this._outer.style.display = node.attrs.hidden ? "none" : "inline-block";
@@ -126,7 +127,7 @@ export class DashDocView {
this._reactionDisposer = reaction(() => ({ dim: [finalLayout[WidthSym](), finalLayout[HeightSym]()], color: finalLayout.color }), ({ dim, color }) => {
this._dashSpan.style.width = this._outer.style.width = Math.max(20, dim[0]) + "px";
this._dashSpan.style.height = this._outer.style.height = Math.max(20, dim[1]) + "px";
- this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+ this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray"));
}, { fireImmediately: true });
const doReactRender = (finalLayout: Doc, resolvedDataDoc: Doc) => {
@@ -144,8 +145,6 @@ export class DashDocView {
addDocTab={this._textBox.props.addDocTab}
pinToPres={returnFalse}
renderDepth={self._textBox.props.renderDepth + 1}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
PanelWidth={finalLayout[WidthSym]}
PanelHeight={finalLayout[HeightSym]}
focus={this.outerFocus}
@@ -155,6 +154,8 @@ 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}
ContentScaling={this.contentScaling}
@@ -177,17 +178,6 @@ export class DashDocView {
this._renderDisposer?.();
this._renderDisposer = reaction(() => {
- // if (!Doc.AreProtosEqual(finalLayout, dashDoc)) {
- // finalLayout.rootDocument = dashDoc.aliasOf; // bcz: check on this ... why is it here?
- // }
- const layoutKey = StrCast(finalLayout.layoutKey);
- const finalKey = layoutKey && StrCast(finalLayout[layoutKey]).split("'")?.[1];
- if (finalLayout !== dashDoc && finalKey) {
- const finalLayoutField = finalLayout[finalKey];
- if (finalLayoutField instanceof ObjectField) {
- finalLayout[finalKey + "-textTemplate"] = ComputedField.MakeFunction(`copyField(this.${finalKey})`, { this: Doc.name });
- }
- }
return { finalLayout, resolvedDataDoc: Cast(finalLayout.resolvedDataDoc, Doc, null) };
},
(res) => doReactRender(res.finalLayout, res.resolvedDataDoc),
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index ce784c3d9..cca7ea013 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -36,20 +36,21 @@ export const marks: { [index: string]: MarkSpec } = {
}],
toDOM(node: any) {
const targetids = node.attrs.allLinks.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.targetId, "");
+ const targethrefs = node.attrs.allLinks.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.href, "");
const linkids = node.attrs.allLinks.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.linkId, "");
return node.attrs.docref && node.attrs.title ?
["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, href: node.attrs.allLinks[0].href, class: "prosemirror-attribution" }, node.attrs.title], ["br"]] :
- node.attrs.allLinks.length === 1 ?
- ["a", { ...node.attrs, class: linkids, dataTargetids: targetids, title: `${node.attrs.title}`, href: node.attrs.allLinks[0].href, style: `text-decoration: ${linkids === " " ? "underline" : undefined}` }, 0] :
- ["div", { class: "prosemirror-anchor" },
- ["span", { class: "prosemirror-linkBtn" },
- ["a", { ...node.attrs, class: linkids, dataTargetids: targetids, title: `${node.attrs.title}` }, 0],
- ["input", { class: "prosemirror-hrefoptions" }],
- ],
- ["div", { class: "prosemirror-links" }, ...node.attrs.allLinks.map((item: { href: string, title: string }) =>
- ["a", { class: "prosemirror-dropdownlink", href: item.href }, item.title]
- )]
- ];
+ //node.attrs.allLinks.length === 1 ?
+ ["a", { ...node.attrs, class: linkids, "data-targetids": targetids, "data-targethrefs": targethrefs, title: `${node.attrs.title}`, href: node.attrs.allLinks[0].href, style: `text-decoration: ${linkids === " " ? "underline" : undefined}` }, 0];
+ // ["div", { class: "prosemirror-anchor" },
+ // ["span", { class: "prosemirror-linkBtn" },
+ // ["a", { ...node.attrs, class: linkids, "data-targetids": targetids, title: `${node.attrs.title}` }, 0],
+ // ["input", { class: "prosemirror-hrefoptions" }],
+ // ],
+ // ["div", { class: "prosemirror-links" }, ...node.attrs.allLinks.map((item: { href: string, title: string }) =>
+ // ["a", { class: "prosemirror-dropdownlink", href: item.href }, item.title]
+ // )]
+ // ];
}
},
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 1616500f6..64f7d27e5 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -148,7 +148,7 @@ export const nodes: { [index: string]: NodeSpec } = {
alt: { default: null },
title: { default: null },
float: { default: "left" },
- location: { default: "onRight" },
+ location: { default: "add:right" },
docid: { default: "" }
},
group: "inline",
@@ -177,7 +177,7 @@ export const nodes: { [index: string]: NodeSpec } = {
height: { default: 100 },
title: { default: null },
float: { default: "right" },
- location: { default: "onRight" },
+ location: { default: "add:right" },
hidden: { default: false },
fieldKey: { default: "" },
docid: { default: "" },
diff --git a/src/client/views/nodes/formattedText/prosemirrorPatches.js b/src/client/views/nodes/formattedText/prosemirrorPatches.js
index 0969ea4ef..746c93868 100644
--- a/src/client/views/nodes/formattedText/prosemirrorPatches.js
+++ b/src/client/views/nodes/formattedText/prosemirrorPatches.js
@@ -146,7 +146,9 @@ function isInSetWithAttrs(mark, set, attrs) {
for (var i = 0; i < set.length; i++) {
if (set[i].type == mark) {
if (Array.from(Object.keys(attrs)).reduce((p, akey) => {
- return p && JSON.stringify(set[i].attrs[akey]) === JSON.stringify(attrs[akey]);
+ if (p && JSON.stringify(set[i].attrs[akey]) === JSON.stringify(attrs[akey])) return true;
+ set[i].attrs.allLinks = Array.from(set[i].attrs.allLinks).filter(a => !Array.from(attrs.allLinks.map(al => al.targetId)).includes(a.targetId) || !Array.from(attrs.allLinks.map(al => al.linkId).includes(a.linkId)))
+ return false;
}, true)) {
return set[i];
}
@@ -178,7 +180,7 @@ function removeMarkWithAttrs(tr, from, to, mark, attrs) {
var style = toRemove[i], found$1 = (void 0);
for (var j = 0; j < matched.length; j++) {
var m = matched[j];
- if (m.step == step - 1 && style.eq(matched[j].style)) { found$1 = m; }
+ if (m.step == step - 1 /*&& style.eq(matched[j].style)*/) { found$1 = m; } // bcz: not sure what this is even trying to do, but style.eq doesn't seem to exist anymore
}
if (found$1) {
found$1.to = end;