aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/DictationManager.ts7
-rw-r--r--src/client/util/DragManager.ts6
-rw-r--r--src/client/util/DropConverter.ts30
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx8
-rw-r--r--src/client/util/Import & Export/ImageUtils.ts12
-rw-r--r--src/client/util/InteractionUtils.tsx5
-rw-r--r--src/client/util/ProseMirrorEditorView.tsx74
-rw-r--r--src/client/util/RichTextMenu.tsx88
-rw-r--r--src/client/util/RichTextRules.ts108
-rw-r--r--src/client/util/RichTextSchema.tsx198
-rw-r--r--src/client/util/request-image-size.js2
-rw-r--r--src/client/util/type_decls.d1
12 files changed, 283 insertions, 256 deletions
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 3d8f2d234..3394cb93d 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -325,15 +325,14 @@ export namespace DictationManager {
["open fields", {
action: (target: DocumentView) => {
- const kvp = Docs.Create.KVPDocument(target.props.Document, { width: 300, height: 300 });
+ const kvp = Docs.Create.KVPDocument(target.props.Document, { _width: 300, _height: 300 });
target.props.addDocTab(kvp, target.props.DataDoc, "onRight");
}
}],
["new outline", {
action: (target: DocumentView) => {
- const newBox = Docs.Create.TextDocument({ width: 400, height: 200, title: "My Outline" });
- newBox.autoHeight = true;
+ const newBox = Docs.Create.TextDocument("", { _width: 400, _height: 200, title: "My Outline", _autoHeight: true });
const proto = newBox.proto!;
const prompt = "Press alt + r to start dictating here...";
const head = 3;
@@ -379,7 +378,7 @@ export namespace DictationManager {
expression: /view as (freeform|stacking|masonry|schema|tree)/g,
action: (target: DocumentView, matches: RegExpExecArray) => {
const mode = CollectionViewType.valueOf(matches[1]);
- mode && (target.props.Document.viewType = mode);
+ mode && (target.props.Document._viewType = mode);
},
restrictTo: [DocumentType.COL]
}
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index df2f5fe3c..c05a2de96 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -94,6 +94,7 @@ export namespace DragManager {
readonly x: number,
readonly y: number,
readonly complete: DragCompleteEvent,
+ readonly shiftKey: boolean,
readonly altKey: boolean,
readonly metaKey: boolean,
readonly ctrlKey: boolean
@@ -205,7 +206,7 @@ export namespace DragManager {
// drag a button template and drop a new button
export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) {
const finishDrag = (e: DragCompleteEvent) => {
- const bd = Docs.Create.ButtonDocument({ width: 150, height: 50, title: title });
+ const bd = Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: title });
bd.onClick = ScriptField.MakeScript(script);
params.map(p => Object.keys(vars).indexOf(p) !== -1 && (Doc.GetProto(bd)[p] = new PrefetchProxy(vars[p] as Doc)));
initialize && initialize(bd);
@@ -340,7 +341,7 @@ export namespace DragManager {
if (dragData instanceof DocumentDragData) {
dragData.userDropAction = e.ctrlKey ? "alias" : undefined;
}
- if (e.shiftKey && CollectionDockingView.Instance) {
+ if (e.shiftKey && CollectionDockingView.Instance && dragData.droppedDocuments.length === 1) {
AbortDrag();
finishDrag?.(new DragCompleteEvent(true, dragData));
CollectionDockingView.Instance.StartOtherDrag({
@@ -409,6 +410,7 @@ export namespace DragManager {
x: e.x,
y: e.y,
complete: complete,
+ shiftKey: e.shiftKey,
altKey: e.altKey,
metaKey: e.metaKey,
ctrlKey: e.ctrlKey
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index ff0e19347..d0f1d86cb 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -1,26 +1,31 @@
import { DragManager } from "./DragManager";
-import { CollectionViewType } from "../views/collections/CollectionView";
import { Doc, DocListCast } from "../../new_fields/Doc";
import { DocumentType } from "../documents/DocumentTypes";
import { ObjectField } from "../../new_fields/ObjectField";
import { StrCast } from "../../new_fields/Types";
import { Docs } from "../documents/Documents";
-import { ScriptField } from "../../new_fields/ScriptField";
+import { ScriptField, ComputedField } from "../../new_fields/ScriptField";
+import { RichTextField } from "../../new_fields/RichTextField";
+import { ImageField } from "../../new_fields/URLField";
-export function makeTemplate(doc: Doc, suppressTitle = false): boolean {
- const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
+export function makeTemplate(doc: Doc): boolean {
+ const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
const layout = StrCast(layoutDoc.layout).match(/fieldKey={'[^']*'}/)![0];
const fieldKey = layout.replace("fieldKey={'", "").replace(/'}$/, "");
const docs = DocListCast(layoutDoc[fieldKey]);
let any = false;
docs.forEach(d => {
if (!StrCast(d.title).startsWith("-")) {
- any = true;
- Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc), suppressTitle);
- } else if (d.type === DocumentType.COL) {
+ any = Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc)) || any;
+ } else if (d.type === DocumentType.COL || d.data instanceof RichTextField) {
any = makeTemplate(d) || any;
}
});
+ if (layoutDoc[fieldKey] instanceof RichTextField || layoutDoc[fieldKey] instanceof ImageField) {
+ if (!StrCast(layoutDoc.title).startsWith("-")) {
+ any = Doc.MakeMetadataFieldTemplate(layoutDoc, Doc.GetProto(layoutDoc));
+ }
+ }
return any;
}
export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
@@ -28,13 +33,14 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
let dbox = doc;
// bcz: isButtonBar is intended to allow a collection of linear buttons to be dropped and nested into another collection of buttons... it's not being used yet, and isn't very elegant
if (!doc.onDragStart && !doc.onClick && !doc.isButtonBar) {
- const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
- if (layoutDoc.type === DocumentType.COL) {
- layoutDoc.isTemplateDoc = makeTemplate(layoutDoc);
+ const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
+ if (layoutDoc.type === DocumentType.COL || layoutDoc.type === DocumentType.TEXT || layoutDoc.type === DocumentType.IMG) {
+ makeTemplate(layoutDoc);
} else {
- layoutDoc.isTemplateDoc = (layoutDoc.type === DocumentType.TEXT || layoutDoc.layout instanceof Doc) && !data.userDropAction;
+ (layoutDoc.layout instanceof Doc) && !data.userDropAction;
}
- dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: layoutDoc.isTemplateDoc ? "font" : "bolt" });
+ layoutDoc.isTemplateDoc = true;
+ dbox = Docs.Create.FontIconDocument({ _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: layoutDoc.isTemplateDoc ? "font" : "bolt" });
dbox.dragFactory = layoutDoc;
dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined;
dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 5b5bffd8c..071015193 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -116,13 +116,13 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
formData.append(Utils.GenerateGuid(), file);
});
- collector.push(...(await Networking.PostFormDataToServer("/upload", formData)));
+ collector.push(...(await Networking.PostFormDataToServer("/uploadFormData", formData)));
runInAction(() => this.completed += batch.length);
});
await Promise.all(uploads.map(async ({ name, type, clientAccessPath, exifData }) => {
const path = Utils.prepend(clientAccessPath);
- const document = await Docs.Get.DocumentFromType(type, path, { width: 300, title: name });
+ const document = await Docs.Get.DocumentFromType(type, path, { _width: 300, title: name });
const { data, error } = exifData;
if (document) {
Doc.GetProto(document).exif = error || Docs.Get.DocumentHierarchyFromJson(data);
@@ -145,8 +145,8 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
const offset: number = this.persistent ? (height === 0 ? 0 : height + 30) : 0;
const options: DocumentOptions = {
title: `Import of ${directory}`,
- width: 1105,
- height: 500,
+ _width: 1105,
+ _height: 500,
x: NumCast(doc.x),
y: NumCast(doc.y) + offset
};
diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts
index 6a9486f83..ff909cc6b 100644
--- a/src/client/util/Import & Export/ImageUtils.ts
+++ b/src/client/util/Import & Export/ImageUtils.ts
@@ -14,9 +14,17 @@ export namespace ImageUtils {
return false;
}
const source = field.url.href;
- const response = await Networking.PostToServer("/inspectImage", { source });
- const { error, data } = response.exifData;
+ const {
+ contentSize,
+ nativeWidth,
+ nativeHeight,
+ exifData: { error, data }
+ } = await Networking.PostToServer("/inspectImage", { source });
document.exif = error || Docs.Get.DocumentHierarchyFromJson(data);
+ const proto = Doc.GetProto(document);
+ proto["data-nativeWidth"] = nativeWidth;
+ proto["data-nativeHeight"] = nativeHeight;
+ proto.contentSize = contentSize;
return data !== undefined;
};
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 1fe95474c..7194feb2e 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -63,7 +63,7 @@ export namespace InteractionUtils {
export function GetMyTargetTouches(mte: InteractionUtils.MultiTouchEvent<React.TouchEvent | TouchEvent>, prevPoints: Map<number, React.Touch>, ignorePen: boolean): React.Touch[] {
const myTouches = new Array<React.Touch>();
for (const pt of mte.touches) {
- if (!ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) {
+ if (!ignorePen || ((pt as any).radiusX > 1 && (pt as any).radiusY > 1)) {
for (const tPt of mte.targetTouches) {
if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) {
if (pt && prevPoints.has(pt.identifier)) {
@@ -73,6 +73,9 @@ export namespace InteractionUtils {
}
}
}
+ // if (mte.touches.length !== myTouches.length) {
+ // throw Error("opo")
+ // }
return myTouches;
}
diff --git a/src/client/util/ProseMirrorEditorView.tsx b/src/client/util/ProseMirrorEditorView.tsx
deleted file mode 100644
index b42adfbb4..000000000
--- a/src/client/util/ProseMirrorEditorView.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-import React from "react";
-import { EditorView } from "prosemirror-view";
-import { EditorState } from "prosemirror-state";
-
-export interface ProseMirrorEditorViewProps {
- /* EditorState instance to use. */
- editorState: EditorState;
- /* Called when EditorView produces new EditorState. */
- onEditorState: (editorState: EditorState) => any;
-}
-
-/**
- * This wraps ProseMirror's EditorView into React component.
- * This code was found on https://discuss.prosemirror.net/t/using-with-react/904
- */
-export class ProseMirrorEditorView extends React.Component<ProseMirrorEditorViewProps> {
-
- private _editorView?: EditorView;
-
- _createEditorView = (element: HTMLDivElement | null) => {
- if (element !== null) {
- this._editorView = new EditorView(element, {
- state: this.props.editorState,
- dispatchTransaction: this.dispatchTransaction,
- });
- }
- }
-
- dispatchTransaction = (tx: any) => {
- // In case EditorView makes any modification to a state we funnel those
- // modifications up to the parent and apply to the EditorView itself.
- const editorState = this.props.editorState.apply(tx);
- if (this._editorView) {
- this._editorView.updateState(editorState);
- }
- this.props.onEditorState(editorState);
- }
-
- focus() {
- if (this._editorView) {
- this._editorView.focus();
- }
- }
-
- componentWillReceiveProps(nextProps: { editorState: EditorState<any>; }) {
- // In case we receive new EditorState through props — we apply it to the
- // EditorView instance.
- if (this._editorView) {
- if (nextProps.editorState !== this.props.editorState) {
- this._editorView.updateState(nextProps.editorState);
- }
- }
- }
-
- componentWillUnmount() {
- if (this._editorView) {
- this._editorView.destroy();
- }
- }
-
- shouldComponentUpdate() {
- // Note that EditorView manages its DOM itself so we'd ratrher don't mess
- // with it.
- return false;
- }
-
- render() {
- // Render just an empty div which is then used as a container for an
- // EditorView instance.
- return (
- <div ref={this._createEditorView} />
- );
- }
-} \ No newline at end of file
diff --git a/src/client/util/RichTextMenu.tsx b/src/client/util/RichTextMenu.tsx
index f070589ae..e07efe056 100644
--- a/src/client/util/RichTextMenu.tsx
+++ b/src/client/util/RichTextMenu.tsx
@@ -73,7 +73,7 @@ export default class RichTextMenu extends AntimodeMenu {
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: "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 },
@@ -312,22 +312,22 @@ export default class RichTextMenu extends AntimodeMenu {
}
return (
- <button className={"antimodeMenu-button" + (isActive ? " active" : "")} title={title} onPointerDown={onClick}>
+ <button className={"antimodeMenu-button" + (isActive ? " active" : "")} key={title} title={title} onPointerDown={onClick}>
<FontAwesomeIcon icon={faIcon as IconProp} size="lg" />
</button>
);
}
- createMarksDropdown(activeOption: string, options: { mark: Mark | null, title: string, label: string, command: (mark: Mark, view: EditorView) => void, hidden?: boolean, style?: {} }[]): JSX.Element {
+ createMarksDropdown(activeOption: string, options: { mark: Mark | null, title: string, label: string, command: (mark: Mark, view: EditorView) => void, hidden?: boolean, style?: {} }[], key: string): JSX.Element {
const items = options.map(({ title, label, hidden, style }) => {
if (hidden) {
return label === activeOption ?
- <option value={label} title={title} style={style ? style : {}} selected hidden>{label}</option> :
- <option value={label} title={title} style={style ? style : {}} hidden>{label}</option>;
+ <option value={label} title={title} key={label} style={style ? style : {}} selected hidden>{label}</option> :
+ <option value={label} title={title} key={label} style={style ? style : {}} hidden>{label}</option>;
}
return label === activeOption ?
- <option value={label} title={title} style={style ? style : {}} selected>{label}</option> :
- <option value={label} title={title} style={style ? style : {}}>{label}</option>;
+ <option value={label} title={title} key={label} style={style ? style : {}} selected>{label}</option> :
+ <option value={label} title={title} key={label} style={style ? style : {}}>{label}</option>;
});
const self = this;
@@ -340,19 +340,19 @@ export default class RichTextMenu extends AntimodeMenu {
}
});
}
- return <select onChange={onChange}>{items}</select>;
+ return <select onChange={onChange} key={key}>{items}</select>;
}
- createNodesDropdown(activeOption: string, options: { node: NodeType | any | null, title: string, label: string, command: (node: NodeType | any) => void, hidden?: boolean, style?: {} }[]): JSX.Element {
+ createNodesDropdown(activeOption: string, options: { node: NodeType | any | null, title: string, label: string, command: (node: NodeType | any) => void, hidden?: boolean, style?: {} }[], key: string): JSX.Element {
const items = options.map(({ title, label, hidden, style }) => {
if (hidden) {
return label === activeOption ?
- <option value={label} title={title} style={style ? style : {}} selected hidden>{label}</option> :
- <option value={label} title={title} style={style ? style : {}} hidden>{label}</option>;
+ <option value={label} title={title} key={label} style={style ? style : {}} selected hidden>{label}</option> :
+ <option value={label} title={title} key={label} style={style ? style : {}} hidden>{label}</option>;
}
return label === activeOption ?
- <option value={label} title={title} style={style ? style : {}} selected>{label}</option> :
- <option value={label} title={title} style={style ? style : {}}>{label}</option>;
+ <option value={label} title={title} key={label} style={style ? style : {}} selected>{label}</option> :
+ <option value={label} title={title} key={label} style={style ? style : {}}>{label}</option>;
});
const self = this;
@@ -363,31 +363,15 @@ export default class RichTextMenu extends AntimodeMenu {
}
});
}
- return <select onChange={e => onChange(e.target.value)}>{items}</select>;
+ return <select onChange={e => onChange(e.target.value)} key={key}>{items}</select>;
}
changeFontSize = (mark: Mark, view: EditorView) => {
- const size = mark.attrs.fontSize;
- if (this.editorProps) {
- const ruleProvider = this.editorProps.ruleProvider;
- const heading = NumCast(this.editorProps.Document.heading);
- if (ruleProvider && heading) {
- ruleProvider["ruleSize_" + heading] = size;
- }
- }
- this.setMark(view.state.schema.marks.pFontSize.create({ fontSize: size }), view.state, view.dispatch);
+ this.setMark(view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize }), view.state, view.dispatch);
}
changeFontFamily = (mark: Mark, view: EditorView) => {
- const fontName = mark.attrs.family;
- if (this.editorProps) {
- const ruleProvider = this.editorProps.ruleProvider;
- const heading = NumCast(this.editorProps.Document.heading);
- if (ruleProvider && heading) {
- ruleProvider["ruleFont_" + heading] = fontName;
- }
- }
- this.setMark(view.state.schema.marks.pFontFamily.create({ family: fontName }), view.state, view.dispatch);
+ this.setMark(view.state.schema.marks.pFontFamily.create({ family: mark.attrs.family }), view.state, view.dispatch);
}
// TODO: remove doesn't work
@@ -472,7 +456,7 @@ export default class RichTextMenu extends AntimodeMenu {
</div>;
return (
- <ButtonDropdown view={this.view} button={button} dropdownContent={dropdownContent} />
+ <ButtonDropdown view={this.view} key={"brush dropdown"} button={button} dropdownContent={dropdownContent} />
);
}
@@ -535,21 +519,21 @@ export default class RichTextMenu extends AntimodeMenu {
</button>;
const dropdownContent =
- <div className="dropdown">
+ <div className="dropdown" >
<p>Change font color:</p>
<div className="color-wrapper">
{this.fontColors.map(color => {
if (color) {
return this.activeFontColor === color ?
- <button className="color-button active" style={{ backgroundColor: color }} onPointerDown={e => changeColor(e, color)}></button> :
- <button className="color-button" style={{ backgroundColor: color }} onPointerDown={e => changeColor(e, color)}></button>;
+ <button className="color-button active" key={"active" + color} style={{ backgroundColor: color }} onPointerDown={e => changeColor(e, color)}></button> :
+ <button className="color-button" key={"other" + color} style={{ backgroundColor: color }} onPointerDown={e => changeColor(e, color)}></button>;
}
})}
</div>
</div>;
return (
- <ButtonDropdown view={this.view} button={button} dropdownContent={dropdownContent} />
+ <ButtonDropdown view={this.view} key={"color dropdown"} button={button} dropdownContent={dropdownContent} />
);
}
@@ -582,7 +566,7 @@ export default class RichTextMenu extends AntimodeMenu {
}
const button =
- <button className="antimodeMenu-button color-preview-button" title="" onPointerDown={onHighlightClick}>
+ <button className="antimodeMenu-button color-preview-button" title="" key="highilghter-button" onPointerDown={onHighlightClick}>
<FontAwesomeIcon icon="highlighter" size="lg" />
<div className="color-preview" style={{ backgroundColor: this.activeHighlightColor }}></div>
</button>;
@@ -594,15 +578,15 @@ export default class RichTextMenu extends AntimodeMenu {
{this.highlightColors.map(color => {
if (color) {
return this.activeHighlightColor === color ?
- <button className="color-button active" style={{ backgroundColor: color }} onPointerDown={e => changeHighlight(e, color)}>{color === "transparent" ? "X" : ""}</button> :
- <button className="color-button" style={{ backgroundColor: color }} onPointerDown={e => changeHighlight(e, color)}>{color === "transparent" ? "X" : ""}</button>;
+ <button className="color-button active" key={`active ${color}`} style={{ backgroundColor: color }} onPointerDown={e => changeHighlight(e, color)}>{color === "transparent" ? "X" : ""}</button> :
+ <button className="color-button" key={`inactive ${color}`} style={{ backgroundColor: color }} onPointerDown={e => changeHighlight(e, color)}>{color === "transparent" ? "X" : ""}</button>;
}
})}
</div>
</div>;
return (
- <ButtonDropdown view={this.view} button={button} dropdownContent={dropdownContent} />
+ <ButtonDropdown view={this.view} key={"highlighter"} button={button} dropdownContent={dropdownContent} />
);
}
@@ -635,7 +619,7 @@ export default class RichTextMenu extends AntimodeMenu {
</div>;
return (
- <ButtonDropdown view={this.view} button={button} dropdownContent={dropdownContent} openDropdownOnButton={true} />
+ <ButtonDropdown view={this.view} key={"link button"} button={button} dropdownContent={dropdownContent} openDropdownOnButton={true} />
);
}
@@ -773,7 +757,7 @@ export default class RichTextMenu extends AntimodeMenu {
render() {
- const row1 = <div className="antimodeMenu-row">{[
+ const row1 = <div className="antimodeMenu-row" key="row1">{[
this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)),
this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)),
this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)),
@@ -787,14 +771,14 @@ export default class RichTextMenu extends AntimodeMenu {
this.createButton("indent", "Summarize", undefined, this.insertSummarizer),
]}</div>;
- const row2 = <div className="antimodeMenu-row row-2">
- <div>
- {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions),
- this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions),
- this.createNodesDropdown(this.activeListType, this.listTypeOptions)]}
+ const row2 = <div className="antimodeMenu-row row-2" key="antimodemenu row2">
+ <div key="row">
+ {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size"),
+ this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family"),
+ this.createNodesDropdown(this.activeListType, this.listTypeOptions, "nodes")]}
</div>
- <div>
- <button className="antimodeMenu-button" title="Pin menu" onClick={this.toggleMenuPin} style={this.Pinned ? { backgroundColor: "#121212" } : {}}>
+ <div key="button">
+ <button className="antimodeMenu-button" key="pin menu" title="Pin menu" onClick={this.toggleMenuPin} style={this.Pinned ? { backgroundColor: "#121212" } : {}}>
<FontAwesomeIcon icon="thumbtack" size="lg" style={{ transition: "transform 0.1s", transform: this.Pinned ? "rotate(45deg)" : "" }} />
</button>
{this.getDragger()}
@@ -864,12 +848,12 @@ class ButtonDropdown extends React.Component<ButtonDropdownProps> {
</button> :
<>
{this.props.button}
- <button className="dropdown-button antimodeMenu-button" onPointerDown={this.onDropdownClick}>
+ <button className="dropdown-button antimodeMenu-button" key="antimodebutton" onPointerDown={this.onDropdownClick}>
<FontAwesomeIcon icon="caret-down" size="sm" />
</button>
</>}
- {this.showDropdown ? this.props.dropdownContent : <></>}
+ {this.showDropdown ? this.props.dropdownContent : (null)}
</div>
);
}
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index e20abb04c..8411cc6ee 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -2,14 +2,16 @@ import { textblockTypeInputRule, smartQuotes, emDash, ellipsis, InputRule } from
import { schema } from "./RichTextSchema";
import { wrappingInputRule } from "./prosemirrorPatches";
import { NodeSelection, TextSelection } from "prosemirror-state";
-import { NumCast, Cast } from "../../new_fields/Types";
-import { Doc } from "../../new_fields/Doc";
+import { StrCast, Cast, NumCast } from "../../new_fields/Types";
+import { Doc, DataSym } from "../../new_fields/Doc";
import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
import { Docs, DocUtils } from "../documents/Documents";
import { Id } from "../../new_fields/FieldSymbols";
import { DocServer } from "../DocServer";
import { returnFalse, Utils } from "../../Utils";
import RichTextMenu from "./RichTextMenu";
+import { RichTextField } from "../../new_fields/RichTextField";
+import { ComputedField } from "../../new_fields/ScriptField";
export const inpRules = {
rules: [
@@ -64,35 +66,69 @@ export const inpRules = {
// set the font size using #<font-size>
new InputRule(
- new RegExp(/^%([0-9]+)\s$/),
+ new RegExp(/%([0-9]+)\s$/),
(state, match, start, end) => {
const size = Number(match[1]);
- const ruleProvider = FormattedTextBox.FocusedBox!.props.ruleProvider;
- const heading = NumCast(FormattedTextBox.FocusedBox!.props.Document.heading);
- if (ruleProvider && heading) {
- (Cast(FormattedTextBox.FocusedBox!.props.Document, Doc) as Doc).heading = size;
- return state.tr.deleteRange(start, end);
- }
return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontSize.create({ fontSize: size }));
}),
- // make current selection a hyperlink portal (assumes % was used to initiate an EnteringStyle mode)
+ // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
new InputRule(
- new RegExp(/@$/),
+ new RegExp(/\[\[([a-zA-Z_ \-0-9]*)(:[a-zA-Z_ \-0-9]+)?\]\]$/),
(state, match, start, end) => {
- if (state.selection.to === state.selection.from || !(schema as any).EnteringStyle) return null;
-
- const value = state.doc.textBetween(start, end);
- if (value) {
- DocServer.GetRefField(value).then(docx => {
- const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500, }, value);
- DocUtils.Publish(target, value, returnFalse, returnFalse);
+ if (!match[2]) {
+ const docId = match[1];
+ DocServer.GetRefField(docId).then(docx => {
+ const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: docId, _width: 500, _height: 500, _LODdisable: true, }, docId);
+ DocUtils.Publish(target, docId, returnFalse, returnFalse);
DocUtils.MakeLink({ doc: (schema as any).Document }, { doc: target }, "portal link", "");
});
- const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + value), location: "onRight", title: value, targetId: value });
- return state.tr.addMark(start, end, link);
+ const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + docId), location: "onRight", title: docId, targetId: docId });
+ return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 2).addMark(start, end - 3, link);
}
- return state.tr;
+ const fieldView = state.schema.nodes.dashField.create({ fieldKey: match[2]?.substring(1), docid: match[1] });
+ return state.tr.deleteRange(start, end).insert(start, fieldView);
+ }),
+ // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
+ new InputRule(
+ new RegExp(/\{\{([a-zA-Z_ \-0-9]*)(:[a-zA-Z_ \-0-9]+)?\}\}$/),
+ (state, match, start, end) => {
+ const docId = match[1];
+ DocServer.GetRefField(docId).then(docx => {
+ if (!(docx instanceof Doc && docx)) {
+ const docx = Docs.Create.FreeformDocument([], { title: docId, _width: 500, _height: 500, _LODdisable: true }, docId);
+ DocUtils.Publish(docx, docId, returnFalse, returnFalse);
+ }
+ });
+ const node = (state.doc.resolve(start) as any).nodeAfter;
+ const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 75, title: "dashDoc", docid: docId, float: "right", fieldKey: match[2]?.substring(1), alias: Utils.GenerateGuid() });
+ const sm = state.storedMarks || undefined;
+ return node ? state.tr.replaceRangeWith(start, end, dashDoc).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
+ }),
+ new InputRule(
+ new RegExp(/##$/),
+ (state, match, start, end) => {
+ const schemaDoc = Doc.GetDataDoc((schema as any).Document);
+ const textDoc = Doc.GetProto(Cast(schemaDoc[DataSym], Doc, null)!);
+ const numInlines = NumCast(textDoc.inlineTextCount);
+ 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: 9, 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.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(this.${inlineFieldKey})`, { this: Doc.name });
+ textDoc[inlineLayoutKey] = FormattedTextBox.LayoutString(inlineFieldKey); // create a layout string for the layout key that will render the annotation text
+ textDoc[inlineFieldKey] = ""; // set a default value for the annotation
+ const node = (state.doc.resolve(start) as any).nodeAfter;
+ const newNode = schema.nodes.dashComment.create({ docid: textDocInline[Id] });
+ const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: "dashDoc", docid: textDocInline[Id], float: "right" });
+ const sm = state.storedMarks || undefined;
+ const replaced = node ? state.tr.insert(start, newNode).replaceRangeWith(start + 1, end + 1, dashDoc).insertText(" ", start + 2).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
+ state.tr;
+ return replaced;
}),
// stop using active style
new InputRule(
@@ -175,12 +211,6 @@ export const inpRules = {
(state, match, start, end) => {
const node = (state.doc.resolve(start) as any).nodeAfter;
const sm = state.storedMarks || undefined;
- const ruleProvider = FormattedTextBox.FocusedBox!.props.ruleProvider;
- const heading = NumCast(FormattedTextBox.FocusedBox!.props.Document.heading);
- if (ruleProvider && heading) {
- ruleProvider["ruleAlign_" + heading] = "center";
- return node ? state.tr.deleteRange(start, end).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
- }
const replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "center" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
state.tr;
return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
@@ -191,12 +221,6 @@ export const inpRules = {
(state, match, start, end) => {
const node = (state.doc.resolve(start) as any).nodeAfter;
const sm = state.storedMarks || undefined;
- const ruleProvider = FormattedTextBox.FocusedBox!.props.ruleProvider;
- const heading = NumCast(FormattedTextBox.FocusedBox!.props.Document.heading);
- if (ruleProvider && heading) {
- ruleProvider["ruleAlign_" + heading] = "left";
- return node ? state.tr.deleteRange(start, end).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
- }
const replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "left" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
state.tr;
return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
@@ -207,29 +231,11 @@ export const inpRules = {
(state, match, start, end) => {
const node = (state.doc.resolve(start) as any).nodeAfter;
const sm = state.storedMarks || undefined;
- const ruleProvider = FormattedTextBox.FocusedBox!.props.ruleProvider;
- const heading = NumCast(FormattedTextBox.FocusedBox!.props.Document.heading);
- if (ruleProvider && heading) {
- ruleProvider["ruleAlign_" + heading] = "right";
- return node ? state.tr.deleteRange(start, end).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
- }
const replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "right" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
state.tr;
return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
}),
new InputRule(
- new RegExp(/%#$/),
- (state, match, start, end) => {
- const target = Docs.Create.TextDocument({ width: 75, height: 35, backgroundColor: "yellow", annotationOn: FormattedTextBox.FocusedBox!.dataDoc, autoHeight: true, fontSize: 9, title: "inline comment" });
- const node = (state.doc.resolve(start) as any).nodeAfter;
- const newNode = schema.nodes.dashComment.create({ docid: target[Id] });
- const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: "dashDoc", docid: target[Id], float: "right" });
- const sm = state.storedMarks || undefined;
- const replaced = node ? state.tr.insert(start, newNode).replaceRangeWith(start + 1, end + 1, dashDoc).insertText(" ", start + 2).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
- state.tr;
- return replaced;//.setSelection(new NodeSelection(replaced.doc.resolve(end)));
- }),
- new InputRule(
new RegExp(/%\(/),
(state, match, start, end) => {
const node = (state.doc.resolve(start) as any).nodeAfter;
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 1f70cafc4..287a1049b 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -1,4 +1,4 @@
-import { reaction, IReactionDisposer } from "mobx";
+import { reaction, IReactionDisposer, observable, runInAction } from "mobx";
import { baseKeymap, toggleMark } from "prosemirror-commands";
import { redo, undo } from "prosemirror-history";
import { keymap } from "prosemirror-keymap";
@@ -8,7 +8,7 @@ import { EditorState, NodeSelection, TextSelection, Plugin } from "prosemirror-s
import { StepMap } from "prosemirror-transform";
import { EditorView } from "prosemirror-view";
import * as ReactDOM from 'react-dom';
-import { Doc, WidthSym, HeightSym } from "../../new_fields/Doc";
+import { Doc, WidthSym, HeightSym, DataSym, Field } from "../../new_fields/Doc";
import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils } from "../../Utils";
import { DocServer } from "../DocServer";
import { DocumentView } from "../views/nodes/DocumentView";
@@ -16,8 +16,12 @@ import { DocumentManager } from "./DocumentManager";
import ParagraphNodeSpec from "./ParagraphNodeSpec";
import { Transform } from "./Transform";
import React = require("react");
-import { BoolCast, NumCast, Cast } from "../../new_fields/Types";
+import { BoolCast, NumCast, StrCast } from "../../new_fields/Types";
import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
+import { ObjectField } from "../../new_fields/ObjectField";
+import { ComputedField } from "../../new_fields/ScriptField";
+import { observer } from "mobx-react";
+import { Id } from "../../new_fields/FieldSymbols";
const blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
@@ -165,7 +169,23 @@ export const nodes: { [index: string]: NodeSpec } = {
float: { default: "right" },
location: { default: "onRight" },
hidden: { default: false },
+ fieldKey: { default: "" },
docid: { default: "" },
+ alias: { default: "" }
+ },
+ group: "inline",
+ draggable: false,
+ toDOM(node) {
+ const attrs = { style: `width: ${node.attrs.width}, height: ${node.attrs.height}` };
+ return ["div", { ...node.attrs, ...attrs }];
+ }
+ },
+
+ dashField: {
+ inline: true,
+ attrs: {
+ fieldKey: { default: "" },
+ docid: { default: "" }
},
group: "inline",
draggable: false,
@@ -306,7 +326,7 @@ export const marks: { [index: string]: MarkSpec } = {
}
}],
toDOM(node: any) {
- return node.attrs.color ? ['span', { style: 'color:' + node.attrs.color }] : ['span', { style: 'color: black' }];
+ return node.attrs.color ? ['span', { style: 'color:' + node.attrs.color }] : ['span', 0];
}
},
@@ -669,7 +689,7 @@ export class DashDocCommentView {
if (target) {
const expand = target.hidden;
const tr = view.state.tr.setNodeMarkup(target.pos, undefined, { ...target.node.attrs, hidden: target.node.attrs.hidden ? false : true });
- view.dispatch(tr.setSelection(TextSelection.create(tr.doc, getPos() + (expand ? 2 : 1)))); // update the attrs
+ view.dispatch(tr.setSelection(TextSelection.create(tr.doc, getPos() + (expand ? 2 : 1)))); // update the attrs
setTimeout(() => {
expand && DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc));
try { view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.tr.doc, getPos() + (expand ? 2 : 1)))); } catch (e) { }
@@ -678,7 +698,7 @@ export class DashDocCommentView {
e.stopPropagation();
};
this._collapsed.onpointerenter = (e: any) => {
- DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc));
+ DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc, false));
e.preventDefault();
e.stopPropagation();
};
@@ -703,7 +723,7 @@ export class DashDocView {
const { scale, translateX, translateY } = Utils.GetScreenTransform(this._outer);
return new Transform(-translateX, -translateY, 1).scale(1 / this.contentScaling() / scale);
}
- contentScaling = () => NumCast(this._dashDoc!.nativeWidth) > 0 && !this._dashDoc!.ignoreAspect ? this._dashDoc![WidthSym]() / NumCast(this._dashDoc!.nativeWidth) : 1;
+ contentScaling = () => NumCast(this._dashDoc!._nativeWidth) > 0 && !this._dashDoc!.ignoreAspect ? this._dashDoc![WidthSym]() / NumCast(this._dashDoc!._nativeWidth) : 1;
outerFocus = (target: Doc) => this._textBox.props.focus(this._textBox.props.Document); // ideally, this would scroll to show the focus target
constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
this._textBox = tbox;
@@ -721,12 +741,6 @@ export class DashDocView {
this._dashSpan.style.height = node.attrs.height;
this._dashSpan.style.position = "absolute";
this._dashSpan.style.display = "inline-block";
- const removeDoc = () => {
- const pos = getPos();
- const ns = new NodeSelection(view.state.doc.resolve(pos));
- view.dispatch(view.state.tr.setSelection(ns).deleteSelection());
- return true;
- };
this._dashSpan.onpointerleave = () => {
const ele = document.getElementById("DashDocCommentView-" + node.attrs.docid);
if (ele) {
@@ -739,47 +753,26 @@ export class DashDocView {
(ele as HTMLDivElement).style.backgroundColor = "orange";
}
};
- DocServer.GetRefField(node.attrs.docid).then(async dashDoc => {
- if (dashDoc instanceof Doc) {
- self._dashDoc = dashDoc;
- dashDoc.hideSidebar = true;
- if (node.attrs.width !== dashDoc.width + "px" || node.attrs.height !== dashDoc.height + "px") {
- try { // bcz: an exception will be thrown if two aliases are open at the same time when a doc view comment is made
- view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, { ...node.attrs, width: dashDoc.width + "px", height: dashDoc.height + "px" }));
- } catch (e) {
- console.log(e);
+ const removeDoc = () => {
+ const pos = getPos();
+ const ns = new NodeSelection(view.state.doc.resolve(pos));
+ view.dispatch(view.state.tr.setSelection(ns).deleteSelection());
+ return true;
+ };
+ const alias = node.attrs.alias;
+
+ const docid = node.attrs.docid || tbox.props.DataDoc?.[Id] || tbox.dataDoc?.[Id];
+ DocServer.GetRefField(docid + alias).then(async dashDoc => {
+ if (!(dashDoc instanceof Doc)) {
+ alias && DocServer.GetRefField(docid).then(async dashDocBase => {
+ if (dashDocBase instanceof Doc) {
+ const aliasedDoc = Doc.MakeDelegate(dashDocBase, docid + alias);
+ aliasedDoc.layoutKey = "layout_" + node.attrs.fieldKey;
+ self.doRender(aliasedDoc, removeDoc, node, view, getPos);
}
- }
- this._reactionDisposer && this._reactionDisposer();
- this._reactionDisposer = reaction(() => dashDoc[HeightSym]() + dashDoc[WidthSym](), () => {
- this._dashSpan.style.height = this._outer.style.height = dashDoc[HeightSym]() + "px";
- this._dashSpan.style.width = this._outer.style.width = dashDoc[WidthSym]() + "px";
});
- ReactDOM.render(<DocumentView
- Document={dashDoc}
- LibraryPath={tbox.props.LibraryPath}
- fitToBox={BoolCast(dashDoc.fitToBox)}
- addDocument={returnFalse}
- removeDocument={removeDoc}
- ruleProvider={undefined}
- ScreenToLocalTransform={this.getDocTransform}
- addDocTab={self._textBox.props.addDocTab}
- pinToPres={returnFalse}
- renderDepth={1}
- PanelWidth={self._dashDoc[WidthSym]}
- PanelHeight={self._dashDoc[HeightSym]}
- focus={self.outerFocus}
- backgroundColor={returnEmptyString}
- parentActive={returnFalse}
- whenActiveChanged={returnFalse}
- bringToFront={emptyFunction}
- zoomToScale={emptyFunction}
- getScale={returnOne}
- dontRegisterView={false}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined}
- ContentScaling={this.contentScaling}
- />, this._dashSpan);
+ } else {
+ self.doRender(dashDoc, removeDoc, node, view, getPos);
}
});
const self = this;
@@ -795,11 +788,110 @@ export class DashDocView {
this._outer.appendChild(this._dashSpan);
(this as any).dom = this._outer;
}
+ doRender(dashDoc: Doc, removeDoc: any, node: any, view: any, getPos: any) {
+ this._dashDoc = dashDoc;
+ dashDoc._hideSidebar = true;
+ if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") {
+ try { // bcz: an exception will be thrown if two aliases are open at the same time when a doc view comment is made
+ view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, { ...node.attrs, width: dashDoc._width + "px", height: dashDoc._height + "px" }));
+ } catch (e) {
+ console.log(e);
+ }
+ }
+ const self = this;
+ const finalLayout = Doc.expandTemplateLayout(dashDoc, !Doc.AreProtosEqual(this._textBox.dataDoc, this._textBox.Document) ? this._textBox.dataDoc : undefined);
+ if (!finalLayout) setTimeout(() => self.doRender(dashDoc, removeDoc, node, view, getPos), 0);
+ else {
+ 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._textTemplate = ComputedField.MakeFunction(`copyField(this.${finalKey})`, { this: Doc.name });
+ }
+ }
+ this._reactionDisposer && this._reactionDisposer();
+ this._reactionDisposer = reaction(() => [finalLayout[WidthSym](), finalLayout[HeightSym]()], (dim) => {
+ this._dashSpan.style.width = this._outer.style.width = dim[0] + "px";
+ this._dashSpan.style.height = this._outer.style.height = dim[1] + "px";
+ }, { fireImmediately: true });
+ ReactDOM.render(<DocumentView
+ Document={finalLayout}
+ DataDoc={!node.attrs.docid ? this._textBox.dataDoc : undefined}
+ LibraryPath={this._textBox.props.LibraryPath}
+ fitToBox={BoolCast(dashDoc._fitToBox)}
+ addDocument={returnFalse}
+ removeDocument={removeDoc}
+ ScreenToLocalTransform={this.getDocTransform}
+ addDocTab={this._textBox.props.addDocTab}
+ pinToPres={returnFalse}
+ renderDepth={1}
+ PanelWidth={finalLayout[WidthSym]}
+ PanelHeight={finalLayout[HeightSym]}
+ focus={this.outerFocus}
+ backgroundColor={returnEmptyString}
+ parentActive={returnFalse}
+ whenActiveChanged={returnFalse}
+ bringToFront={emptyFunction}
+ zoomToScale={emptyFunction}
+ getScale={returnOne}
+ dontRegisterView={false}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ ContentScaling={this.contentScaling}
+ />, this._dashSpan);
+ }
+ }
destroy() {
this._reactionDisposer && this._reactionDisposer();
}
}
+
+export class DashFieldView {
+ _fieldWrapper: HTMLDivElement;
+ _labelSpan: HTMLSpanElement;
+ _fieldSpan: HTMLSpanElement;
+ _reactionDisposer: IReactionDisposer | undefined;
+ _textBoxDoc: Doc;
+ @observable _dashDoc: Doc | undefined;
+
+ constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
+ this._textBoxDoc = tbox.props.Document;
+ this._fieldWrapper = document.createElement("div");
+ this._fieldWrapper.style.width = node.attrs.width;
+ this._fieldWrapper.style.height = node.attrs.height;
+ this._fieldWrapper.style.position = "relative";
+ this._fieldWrapper.style.display = "inline";
+
+ this._fieldSpan = document.createElement("span");
+ this._fieldSpan.style.position = "relative";
+ this._fieldSpan.style.display = "inline";
+
+ this._labelSpan = document.createElement("span");
+ this._labelSpan.style.position = "relative";
+ this._labelSpan.style.display = "inline";
+ this._labelSpan.style.fontWeight = "bold";
+ this._labelSpan.style.fontSize = "larger";
+ this._labelSpan.innerHTML = `${node.attrs.fieldKey}: `;
+ if (node.attrs.docid) {
+ const self = this;
+ DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && runInAction(() => self._dashDoc = dashDoc));
+ } else {
+ this._dashDoc = tbox.props.DataDoc || tbox.dataDoc;
+ }
+ this._reactionDisposer?.();
+ this._reactionDisposer = reaction(() => this._dashDoc?.[node.attrs.fieldKey], fval => this._fieldSpan.innerHTML = Field.toString(fval as Field), { fireImmediately: true });
+
+ this._fieldWrapper.appendChild(this._labelSpan);
+ this._fieldWrapper.appendChild(this._fieldSpan);
+ (this as any).dom = this._fieldWrapper;
+ }
+ destroy() {
+ this._reactionDisposer?.();
+ }
+}
+
export class OrderedListView {
update(node: any) {
return false; // if attr's of an ordered_list (e.g., bulletStyle) change, return false forces the dom node to be recreated which is necessary for the bullet labels to update
@@ -964,7 +1056,7 @@ export class SummaryView {
view.dispatch(view.state.tr.
setSelection(textSelection). // select the current summarized text (or where it will be if its collapsed)
replaceSelection(!visible ? new Slice(Fragment.fromArray([]), 0, 0) : node.attrs.text). // collapse/expand it
- setNodeMarkup(getPos(), undefined, attrs)); // update the attrs
+ setNodeMarkup(getPos(), undefined, attrs)); // update the attrs
e.preventDefault();
e.stopPropagation();
this._collapsed.className = this.className(visible);
diff --git a/src/client/util/request-image-size.js b/src/client/util/request-image-size.js
index 27605d167..beb030635 100644
--- a/src/client/util/request-image-size.js
+++ b/src/client/util/request-image-size.js
@@ -38,7 +38,7 @@ module.exports = function requestImageSize(options) {
return reject(new HttpError(res.statusCode, res.statusMessage));
}
- let buffer = new Buffer([]);
+ let buffer = new Buffer.from([]);
let size;
let imageSizeError;
diff --git a/src/client/util/type_decls.d b/src/client/util/type_decls.d
index 622e10960..127f7b798 100644
--- a/src/client/util/type_decls.d
+++ b/src/client/util/type_decls.d
@@ -131,6 +131,7 @@ interface Promise<T> {
declare const Update: unique symbol;
declare const Self: unique symbol;
declare const SelfProxy: unique symbol;
+declare const DataSym: unique symbol;
declare const HandleUpdate: unique symbol;
declare const Id: unique symbol;
declare const OnUpdate: unique symbol;