aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/views/ScriptBox.tsx1
-rw-r--r--src/client/views/collections/TabDocView.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx7
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx144
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx8
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx140
-rw-r--r--src/fields/Doc.ts1
-rw-r--r--src/fields/ScriptField.ts17
-rw-r--r--src/server/websocket.ts4
9 files changed, 159 insertions, 165 deletions
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index 2c185be86..e2b5d8dc3 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -12,7 +12,6 @@ import { CompileScript } from "../util/Scripting";
import { ScriptField } from "../../fields/ScriptField";
import { DragManager } from "../util/DragManager";
import { EditableView } from "./EditableView";
-import { getEffectiveTypeRoots } from "typescript";
export interface ScriptBoxProps {
onSave: (text: string, onError: (error: string) => void) => void;
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 61650da6c..a8a95bf08 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -431,7 +431,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
// Doc.SetNativeWidth(this.props.Document[DataSym], wid);
// Doc.SetNativeHeight(this.props.Document[DataSym], hgt);
}
- }))
+ }));
//
// a preliminary implementation of a dash style sheet for setting rendering properties of documents nested within a Tab
//
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ab0c3964b..fa84e5539 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -222,14 +222,17 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const zsorted = this.childLayoutPairs.map(pair => pair.layout).slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
zsorted.forEach((doc, index) => doc.zIndex = doc.isInkMask ? 5000 : index + 1);
const dvals = CollectionFreeFormDocumentView.getValues(refDoc, NumCast(refDoc.activeFrame, 1000));
- const dropPos = this.Document._currentFrame !== undefined ? [dvals.x, dvals.y] : [NumCast(refDoc.x), NumCast(refDoc.y)];
+ const dropPos = this.Document._currentFrame !== undefined ? [dvals.x || 0, dvals.y || 0] : [NumCast(refDoc.x), NumCast(refDoc.y)];
for (let i = 0; i < docDragData.droppedDocuments.length; i++) {
const d = docDragData.droppedDocuments[i];
const layoutDoc = Doc.Layout(d);
if (this.Document._currentFrame !== undefined) {
CollectionFreeFormDocumentView.setupKeyframes([d], this.Document._currentFrame, false);
const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000));
- CollectionFreeFormDocumentView.setValues(this.Document._currentFrame, d, x + vals.x - dropPos[0], y + vals.y - dropPos[1], vals.h, vals.w, this.Document.editScrollProgressivize ? vals.scroll : undefined, vals.opacity);
+ vals.x = x + (vals.x || 0) - dropPos[0];
+ vals.y = y + (vals.y || 0) - dropPos[1];
+ vals._scrollTop = this.Document.editScrollProgressivize ? vals._scrollTop : undefined;
+ CollectionFreeFormDocumentView.setValues(this.Document._currentFrame, d, vals);
} else {
d.x = x + NumCast(d.x) - dropPos[0];
d.y = y + NumCast(d.y) - dropPos[1];
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 94793ffff..942182191 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,25 +1,23 @@
-import { computed, IReactionDisposer, observable, reaction, trace, action } from "mobx";
+import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
-import { Doc, HeightSym, WidthSym } from "../../../fields/Doc";
+import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
+import { Doc, HeightSym, WidthSym, Opt } from "../../../fields/Doc";
+import { Document } from "../../../fields/documentSchemas";
+import { List } from "../../../fields/List";
+import { listSpec } from "../../../fields/Schema";
+import { ComputedField } from "../../../fields/ScriptField";
import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { TraceMobx } from "../../../fields/util";
+import { numberRange, returnVal } from "../../../Utils";
+import { DocumentType } from "../../documents/DocumentTypes";
import { Transform } from "../../util/Transform";
import { DocComponent } from "../DocComponent";
+import { InkingStroke } from "../InkingStroke";
import "./CollectionFreeFormDocumentView.scss";
-import { DocumentView, DocumentViewProps } from "./DocumentView";
-import React = require("react");
-import { Document } from "../../../fields/documentSchemas";
-import { TraceMobx } from "../../../fields/util";
import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
-import { List } from "../../../fields/List";
-import { numberRange, smoothScroll, returnVal } from "../../../Utils";
-import { ComputedField } from "../../../fields/ScriptField";
-import { listSpec } from "../../../fields/Schema";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { Zoom, Fade, Flip, Rotate, Bounce, Roll, LightSpeed } from 'react-reveal';
+import { DocumentView, DocumentViewProps } from "./DocumentView";
import { PresBox, PresEffect } from "./PresBox";
-import { InkingStroke } from "../InkingStroke";
-import { SnappingManager } from "../../util/SnappingManager";
-import { InkTool } from "../../../fields/InkField";
+import React = require("react");
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined;
@@ -62,46 +60,24 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
@computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, undefined, this.freezeDimensions)); }
@computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, undefined, this.freezeDimensions)); }
+ static animFields = ["_height", "_width", "x", "y", "_scrollTop", "opacity"];
public static getValues(doc: Doc, time: number) {
- const timecode = Math.round(time);
- return ({
- h: Cast(doc["h-indexed"], listSpec("number"), [NumCast(doc._height)]).reduce((p, h, i) => (i <= timecode && h !== undefined) || p === undefined ? h : p, undefined as any as number),
- w: Cast(doc["w-indexed"], listSpec("number"), [NumCast(doc._width)]).reduce((p, w, i) => (i <= timecode && w !== undefined) || p === undefined ? w : p, undefined as any as number),
- x: Cast(doc["x-indexed"], listSpec("number"), [NumCast(doc.x)]).reduce((p, x, i) => (i <= timecode && x !== undefined) || p === undefined ? x : p, undefined as any as number),
- y: Cast(doc["y-indexed"], listSpec("number"), [NumCast(doc.y)]).reduce((p, y, i) => (i <= timecode && y !== undefined) || p === undefined ? y : p, undefined as any as number),
- scroll: Cast(doc["scroll-indexed"], listSpec("number"), [NumCast(doc._scrollTop, 0)]).reduce((p, s, i) => (i <= timecode && s !== undefined) || p === undefined ? s : p, undefined as any as number),
- opacity: Cast(doc["opacity-indexed"], listSpec("number"), [NumCast(doc.opacity, 1)]).reduce((p, o, i) => i <= timecode || p === undefined ? o : p, undefined as any as number),
- });
+ return CollectionFreeFormDocumentView.animFields.reduce((p, val) => {
+ p[val] = Cast(`${val}-indexed`, listSpec("number"), [NumCast(doc[val])]).reduce((p, v, i) => (i <= Math.round(time) && v !== undefined) || p === undefined ? v : p, undefined as any as number);
+ return p;
+ }, {} as { [val: string]: Opt<number> });
}
- public static setValues(time: number, d: Doc, x?: number, y?: number, h?: number, w?: number, scroll?: number, opacity?: number) {
+ public static setValues(time: number, d: Doc, vals: { [val: string]: Opt<number> }) {
const timecode = Math.round(time);
- const hindexed = Cast(d["h-indexed"], listSpec("number"), []).slice();
- const windexed = Cast(d["w-indexed"], listSpec("number"), []).slice();
- const xindexed = Cast(d["x-indexed"], listSpec("number"), []).slice();
- const yindexed = Cast(d["y-indexed"], listSpec("number"), []).slice();
- const oindexed = Cast(d["opacity-indexed"], listSpec("number"), []).slice();
- const scrollIndexed = Cast(d["scroll-indexed"], listSpec("number"), []).slice();
- xindexed[timecode] = x as any as number;
- yindexed[timecode] = y as any as number;
- hindexed[timecode] = h as any as number;
- windexed[timecode] = w as any as number;
- oindexed[timecode] = opacity as any as number;
- if (scroll) scrollIndexed[timecode] = scroll as any as number;
- d["x-indexed"] = new List<number>(xindexed);
- d["y-indexed"] = new List<number>(yindexed);
- d["h-indexed"] = new List<number>(hindexed);
- d["w-indexed"] = new List<number>(windexed);
- d["opacity-indexed"] = new List<number>(oindexed);
- d["scroll-indexed"] = new List<number>(scrollIndexed);
- if (d.appearFrame) {
- if (d.appearFrame === timecode + 1) {
- d["text-color"] = "red";
- } else if (d.appearFrame < timecode + 1) {
- d["text-color"] = "grey";
- } else { d["text-color"] = "black"; }
- } else if (d.appearFrame === 0) {
- }
+ Object.keys(vals).forEach(val => {
+ const findexed = Cast(d[`${val}-indexed`], listSpec("number"), []).slice();
+ findexed[timecode] = vals[val] as any as number;
+ d[`${val}-indexed`] = new List<number>(findexed);
+ });
+ d.appearFrame && (d["text-color"] =
+ d.appearFrame === timecode + 1 ? "red" :
+ d.appearFrame < timecode + 1 ? "grey" : "black");
}
// public static updateScrollframe(doc: Doc, time: number) {
@@ -123,37 +99,26 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static updateKeyframe(docs: Doc[], time: number, targetDoc?: Doc) {
const timecode = Math.round(time);
- docs.forEach(doc => {
- const xindexed = Cast(doc['x-indexed'], listSpec("number"), null);
- const yindexed = Cast(doc['y-indexed'], listSpec("number"), null);
- const hindexed = Cast(doc['h-indexed'], listSpec("number"), null);
- const windexed = Cast(doc['w-indexed'], listSpec("number"), null);
- const opacityindexed = Cast(doc['opacity-indexed'], listSpec("number"), null);
- hindexed?.length <= timecode + 1 && hindexed.push(undefined as any as number);
- windexed?.length <= timecode + 1 && windexed.push(undefined as any as number);
- xindexed?.length <= timecode + 1 && xindexed.push(undefined as any as number);
- yindexed?.length <= timecode + 1 && yindexed.push(undefined as any as number);
- opacityindexed?.length <= timecode + 1 && opacityindexed.push(undefined as any as number);
- if (doc.appearFrame && targetDoc) {
- if (doc.appearFrame === timecode + 1) {
- doc["text-color"] = StrCast(targetDoc["pres-text-color"]);
- } else if (doc.appearFrame < timecode + 1) {
- doc["text-color"] = StrCast(targetDoc["pres-text-viewed-color"]);
- } else { doc["text-color"] = "black"; }
- } else if (doc.appearFrame === 0) {
- doc["text-color"] = "black";
- }
- doc.dataTransition = "all 1s";
- });
- setTimeout(() => docs.forEach(doc => doc.dataTransition = "inherit"), 1010);
+ docs.forEach(action(doc => {
+ doc._viewTransition = doc.dataTransition = "all 1s";
+ doc["text-color"] =
+ !doc.appearFrame || !targetDoc ? "black" :
+ doc.appearFrame === timecode + 1 ? StrCast(targetDoc["pres-text-color"]) :
+ doc.appearFrame < timecode + 1 ? StrCast(targetDoc["pres-text-viewed-color"]) :
+ "black";
+ CollectionFreeFormDocumentView.animFields.forEach(val => {
+ const findexed = Cast(doc[`${val}-indexed`], listSpec("number"), null);
+ findexed?.length <= timecode + 1 && findexed.push(undefined as any as number);
+ });
+ }));
+ setTimeout(() => docs.forEach(doc => { doc._viewTransition = undefined; doc.dataTransition = "inherit"; }), 1010);
}
public static gotoKeyframe(docs: Doc[]) {
- docs.forEach(doc => doc.dataTransition = "all 1s");
- setTimeout(() => docs.forEach(doc => doc.dataTransition = "inherit"), 1010);
+ docs.forEach(doc => doc._viewTransition = doc.dataTransition = "all 1s");
+ setTimeout(() => docs.forEach(doc => { doc._viewTransition = undefined; doc.dataTransition = "inherit" }), 1010);
}
-
public static setupZoom(doc: Doc, targDoc: Doc) {
const width = new List<number>();
const height = new List<number>();
@@ -172,27 +137,12 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static setupKeyframes(docs: Doc[], currTimecode: number, makeAppear: boolean = false) {
docs.forEach(doc => {
if (doc.appearFrame === undefined) doc.appearFrame = currTimecode;
- const curTimecode = currTimecode;
- const xlist = new List<number>(numberRange(currTimecode + 1).map(i => undefined) as any as number[]);
- const ylist = new List<number>(numberRange(currTimecode + 1).map(i => undefined) as any as number[]);
- const wlist = new List<number>(numberRange(currTimecode + 1).map(i => undefined) as any as number[]);
- const hlist = new List<number>(numberRange(currTimecode + 1).map(i => undefined) as any as number[]);
- const olist = new List<number>(numberRange(currTimecode + 1).map(t => !doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1));
- wlist[curTimecode] = NumCast(doc._width);
- hlist[curTimecode] = NumCast(doc._height);
- xlist[curTimecode] = NumCast(doc.x);
- ylist[curTimecode] = NumCast(doc.y);
- doc["x-indexed"] = xlist;
- doc["y-indexed"] = ylist;
- doc["w-indexed"] = wlist;
- doc["h-indexed"] = hlist;
- doc["opacity-indexed"] = olist;
+ if (!doc["opacity-indexed"]) { // opacity is unlike other fields because it's value should not be undefined before it appears to enable it to fade-in
+ const olist = new List<number>(numberRange(currTimecode + 1).map(t => !doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1));
+ doc["opacity-indexed"] = olist;
+ }
+ CollectionFreeFormDocumentView.animFields.forEach(val => doc[val] = ComputedField.MakeInterpolated(val, "activeFrame", doc, currTimecode));
doc.activeFrame = ComputedField.MakeFunction("self.context?._currentFrame||0");
- doc._height = ComputedField.MakeInterpolated("h", "activeFrame");
- doc._width = ComputedField.MakeInterpolated("w", "activeFrame");
- doc.x = ComputedField.MakeInterpolated("x", "activeFrame");
- doc.y = ComputedField.MakeInterpolated("y", "activeFrame");
- doc.opacity = ComputedField.MakeInterpolated("opacity", "activeFrame");
doc.dataTransition = "inherit";
});
}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 0256d394e..c4bb2b8d8 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -12,6 +12,7 @@ import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
import { DocumentLinksButton } from './DocumentLinksButton';
import React = require("react");
import { DocumentViewProps } from './DocumentView';
+import { Id } from '../../../fields/FieldSymbols';
interface Props {
linkDoc?: Doc;
@@ -64,8 +65,11 @@ export class LinkDocPreview extends React.Component<Props> {
runInAction(() => {
this._toolTipText = "";
LinkDocPreview.TargetDoc = this._targetDoc = target;
- if (anchor !== this._targetDoc && anchor && this._targetDoc) {
- this._targetDoc._scrollPreviewY = NumCast(anchor?.y);
+ if (this._targetDoc) {
+ this._targetDoc._scrollToPreviewLinkID = linkDoc?.[Id];
+ if (anchor !== this._targetDoc && anchor) {
+ this._targetDoc._scrollPreviewY = NumCast(anchor?.y);
+ }
}
});
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 125447f28..6768acce4 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -61,6 +61,7 @@ import { DocumentManager } from '../../../util/DocumentManager';
import { CollectionStackingView } from '../../collections/CollectionStackingView';
import { CollectionViewType } from '../../collections/CollectionView';
import { SnappingManager } from '../../../util/SnappingManager';
+import { LinkDocPreview } from '../LinkDocPreview';
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
@@ -98,6 +99,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
private _currentTime: number = 0;
private _linkTime: number | null = null;
private _pause: boolean = false;
+ private _animatingScroll: numher = 0; // hack to prevent scroll values from being written to document when scroll is animating
@computed get _recording() { return this.dataDoc?.audioState === "recording"; }
set _recording(value) {
@@ -815,6 +817,50 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
return this.active();//this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0;
}
+ scrollToLinkId = (linkId: string) => {
+ const findLinkFrag = (frag: Fragment, editor: EditorView) => {
+ const nodes: Node[] = [];
+ let hadStart = start !== 0;
+ frag.forEach((node, index) => {
+ const examinedNode = findLinkNode(node, editor);
+ if (examinedNode?.node.textContent) {
+ nodes.push(examinedNode.node);
+ !hadStart && (start = index + examinedNode.start);
+ hadStart = true;
+ }
+ });
+ return { frag: Fragment.fromArray(nodes), start };
+ };
+ const findLinkNode = (node: Node, editor: EditorView) => {
+ if (!node.isText) {
+ const content = findLinkFrag(node.content, editor);
+ return { node: node.copy(content.frag), start: content.start };
+ }
+ const marks = [...node.marks];
+ const linkIndex = marks.findIndex(mark => mark.type === editor.state.schema.marks.linkAnchor);
+ return linkIndex !== -1 && marks[linkIndex].attrs.allLinks.find((item: { href: string }) => linkId === item.href.replace(/.*\/doc\//, "")) ? { node, start: 0 } : undefined;
+ };
+
+ let start = 0;
+ if (this._editorView && linkId) {
+ const editor = this._editorView;
+ const ret = findLinkFrag(editor.state.doc.content, editor);
+
+ if (ret.frag.size > 2 && ret.start >= 0) {
+ let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start
+ if (ret.frag.firstChild) {
+ selection = TextSelection.between(editor.state.doc.resolve(ret.start), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected
+ }
+ editor.dispatch(editor.state.tr.setSelection(new TextSelection(selection.$from, selection.$from)).scrollIntoView());
+ const mark = editor.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
+ setTimeout(() => editor.dispatch(editor.state.tr.addMark(selection.from, selection.to, mark)), 0);
+ setTimeout(() => this.unhighlightSearchTerms(), 2000);
+ }
+ Doc.SetInPlace(this.layoutDoc, "scrollToLinkID", undefined, false);
+ Doc.SetInPlace(this.layoutDoc, "_scrollToPreviewLinkID", undefined, false);
+ }
+ }
+
componentDidMount() {
this.props.contentsActive?.(this.IsActive);
this._cachedLinks = DocListCast(this.Document.links);
@@ -910,7 +956,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
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));
+ this._disposers.selected = reaction(() => this.props.isSelected(),
+ action((selected) => {
+ this._recording = false;
+ if (RichTextMenu.Instance?.view === this._editorView && !selected) {
+ RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined);
+ }
+ }));
if (!this.props.dontRegisterView) {
this._disposers.record = reaction(() => this._recording,
@@ -924,65 +976,36 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
);
}
- this._disposers.scrollToRegion = reaction(
- () => StrCast(this.layoutDoc.scrollToLinkID),
- async (scrollToLinkID) => {
- const findLinkFrag = (frag: Fragment, editor: EditorView) => {
- const nodes: Node[] = [];
- let hadStart = start !== 0;
- frag.forEach((node, index) => {
- const examinedNode = findLinkNode(node, editor);
- if (examinedNode?.node.textContent) {
- nodes.push(examinedNode.node);
- !hadStart && (start = index + examinedNode.start);
- hadStart = true;
- }
- });
- return { frag: Fragment.fromArray(nodes), start };
- };
- const findLinkNode = (node: Node, editor: EditorView) => {
- if (!node.isText) {
- const content = findLinkFrag(node.content, editor);
- return { node: node.copy(content.frag), start: content.start };
- }
- const marks = [...node.marks];
- const linkIndex = marks.findIndex(mark => mark.type === editor.state.schema.marks.linkAnchor);
- return linkIndex !== -1 && marks[linkIndex].attrs.allLinks.find((item: { href: string }) => scrollToLinkID === item.href.replace(/.*\/doc\//, "")) ? { node, start: 0 } : undefined;
- };
-
- let start = 0;
- if (this._editorView && scrollToLinkID) {
- const editor = this._editorView;
- const ret = findLinkFrag(editor.state.doc.content, editor);
-
- if (ret.frag.size > 2 && ret.start >= 0) {
- let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start
- if (ret.frag.firstChild) {
- selection = TextSelection.between(editor.state.doc.resolve(ret.start), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected
- }
- editor.dispatch(editor.state.tr.setSelection(new TextSelection(selection.$from, selection.$from)).scrollIntoView());
- const mark = editor.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
- setTimeout(() => editor.dispatch(editor.state.tr.addMark(selection.from, selection.to, mark)), 0);
- setTimeout(() => this.unhighlightSearchTerms(), 2000);
- }
- Doc.SetInPlace(this.layoutDoc, "scrollToLinkID", undefined, false);
- }
-
- },
- { fireImmediately: true }
+ this._disposers.previewScrollToRegion = reaction(() => StrCast(this.layoutDoc._scrollToPreviewLinkID),
+ scrollToLinkID => this.props.renderDepth === -1 && scrollToLinkID && this.scrollToLinkId(scrollToLinkID), { fireImmediately: true }
+ );
+ this._disposers.scrollToRegion = reaction(() => StrCast(this.layoutDoc.scrollToLinkID),
+ scrollToLinkID => scrollToLinkID && this.scrollToLinkId(scrollToLinkID), { fireImmediately: true }
);
this._disposers.scroll = reaction(() => NumCast(this.layoutDoc._scrollTop),
- pos => this._scrollRef.current?.scrollTo({ top: pos }), { fireImmediately: true }
+ pos => {
+ const durationMiliStr = StrCast(this.Document._viewTransition).match(/([0-9]*)ms/);
+ const durationSecStr = StrCast(this.Document._viewTransition).match(/([0-9.]*)s/);
+ const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
+ if (duration) {
+ this._animatingScroll++;
+ this._scrollRef.current && smoothScroll(duration, this._scrollRef.current, Math.abs(pos || 0));
+ setTimeout(() => this._animatingScroll--, duration);
+ } else {
+ this._scrollRef.current?.scrollTo({ top: pos });
+ }
+ }, { fireImmediately: true }
);
this._disposers.scrollY = reaction(() => Cast(this.layoutDoc._scrollY, "number", null),
- scrollY => {
- if (scrollY !== undefined) {
- const delay = this._scrollRef.current ? 0 : 250; // wait for mainCont and try again to scroll
- const durationStr = StrCast(this.Document._viewTransition).match(/([0-9]*)ms/);
- const duration = durationStr ? Number(durationStr[1]) : 1000;
- setTimeout(() => this._scrollRef.current && smoothScroll(duration, this._scrollRef.current, Math.abs(scrollY || 0)), delay);
- setTimeout(() => { this.Document._scrollTop = scrollY; this.Document._scrollY = undefined; }, duration + delay);
- }
+ pos => {
+ this.Document._scrollY = undefined;
+ pos !== undefined && setTimeout(() => this.layoutDoc._scrollTop = pos, this._scrollRef.current ? 0 : 250);
+ }, { fireImmediately: true }
+ );
+ this._disposers.scrollPreviewY = reaction(() => Cast(this.layoutDoc.scrollPreviewY, "number", null),
+ pos => {
+ this.Document.scrollPreviewY = undefined;
+ pos !== undefined && setTimeout(() => this.layoutDoc._scrollTop = pos, this._scrollRef.current ? 0 : 250);
}, { fireImmediately: true }
);
@@ -1233,6 +1256,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
componentWillUnmount() {
this.endUndoTypingBatch();
+ this.unhighlightSearchTerms();
Object.values(this._disposers).forEach(disposer => disposer?.());
this._editorView?.destroy();
FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none");
@@ -1529,7 +1553,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
eve.stopPropagation(); // drag n drop of text within text note will generate a new note if not caughst, as will dragging in from outside of Dash.
}
onscrolled = (ev: React.UIEvent) => {
- this.layoutDoc._scrollTop = this._scrollRef.current!.scrollTop;
+ if (!LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc && !this._animatingScroll) {
+ this.layoutDoc._scrollTop = this._scrollRef.current!.scrollTop;
+ }
}
@action
tryUpdateHeight(limitHeight?: number) {
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index ffb1bbf83..fdea47c84 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -36,6 +36,7 @@ export namespace Field {
export function toScriptString(field: Field): string {
if (typeof field === "string") return `"${field}"`;
if (typeof field === "number" || typeof field === "boolean") return String(field);
+ if (field === undefined || field === null) return "null";
return field[ToScriptString]();
}
export function toString(field: Field): string {
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index 024017302..c949e0791 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -7,7 +7,9 @@ import { Doc, Field, Opt } from "./Doc";
import { Plugins, setter } from "./util";
import { computedFn } from "mobx-utils";
import { ProxyField } from "./Proxy";
-import { Cast } from "./Types";
+import { Cast, NumCast } from "./Types";
+import { List } from "./List";
+import { numberRange } from "../Utils";
function optional(propSchema: PropSchema) {
return custom(value => {
@@ -188,12 +190,21 @@ export class ComputedField extends ScriptField {
const compiled = ScriptField.CompileScript(script, params, true, capturedVariables);
return compiled.compiled ? new ComputedField(compiled) : undefined;
}
- public static MakeInterpolated(fieldKey: string, interpolatorKey: string) {
+ public static MakeInterpolated(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) {
+ if (!doc[`${fieldKey}-indexed`]) {
+ const flist = new List<number>(numberRange(curTimecode + 1).map(i => undefined) as any as number[]);
+ flist[curTimecode] = NumCast(doc[fieldKey]);
+ doc[`${fieldKey}-indexed`] = flist;
+ }
const getField = ScriptField.CompileScript(`getIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey})`, {}, true, {});
- const setField = ScriptField.CompileScript(`(self['${fieldKey}-indexed'])[self.${interpolatorKey}] = value`, { value: "any" }, true, {});
+ const setField = ScriptField.CompileScript(`setIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey}, value)`, { value: "any" }, true, {});
return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined;
}
}
+Scripting.addGlobal(function setIndexVal(list: any[], index: number, value: any) {
+ while (list.length <= index) list.push(undefined);
+ list[index] = value;
+}, "sets the value at a given index of a list", "(list: any[], index: number, value: any)");
Scripting.addGlobal(function getIndexVal(list: any[], index: number) {
return list?.reduce((p, x, i) => (i <= index && x !== undefined) || p === undefined ? x : p, undefined as any);
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index 7d111f359..6850f0601 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -279,13 +279,13 @@ export namespace WebSocket {
const updatefield = Array.from(Object.keys(diff.diff.$set))[0];
const newListItems = diff.diff.$set[updatefield].fields;
const curList = (curListItems as any)?.fields?.[updatefield.replace("fields.", "")]?.fields.filter((item: any) => item !== undefined) || [];
- diff.diff.$set[updatefield].fields = [...curList, ...newListItems.filter((newItem: any) => newItem && !curList.some((curItem: any) => curItem.fieldId ? curItem.fieldId === newItem.fieldId : curItem.heading ? curItem.heading === newItem.heading : curItem === newItem))];
+ diff.diff.$set[updatefield].fields = [...curList, ...newListItems.filter((newItem: any) => newItem === null || !curList.some((curItem: any) => curItem.fieldId ? curItem.fieldId === newItem.fieldId : curItem.heading ? curItem.heading === newItem.heading : curItem === newItem))];
const sendBack = diff.diff.length !== diff.diff.$set[updatefield].fields.length;
delete diff.diff.length;
Database.Instance.update(diff.id, diff.diff,
() => {
if (sendBack) {
- console.log("RET BACK");
+ console.log("Warning: list modified during update. Composite list is being returned.");
const id = socket.id;
socket.id = "";
socket.broadcast.emit(MessageStore.UpdateField.Message, diff);