aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Utils.ts24
-rw-r--r--src/client/views/AntimodeMenu.scss2
-rw-r--r--src/client/views/DocumentDecorations.scss25
-rw-r--r--src/client/views/DocumentDecorations.tsx426
-rw-r--r--src/client/views/GlobalKeyHandler.ts2
-rw-r--r--src/client/views/InkStrokeProperties.ts2
-rw-r--r--src/client/views/PropertiesView.tsx3
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx5
-rw-r--r--src/client/views/nodes/DocumentView.tsx4
-rw-r--r--src/client/views/nodes/EquationBox.tsx6
-rw-r--r--src/client/views/nodes/FunctionPlotBox.tsx3
-rw-r--r--src/client/views/nodes/ImageBox.scss2
-rw-r--r--src/client/views/nodes/LinkDocPreview.scss1
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx19
-rw-r--r--src/client/views/nodes/PDFBox.tsx15
-rw-r--r--src/client/views/nodes/WebBox.tsx101
-rw-r--r--src/client/views/nodes/formattedText/EquationView.tsx16
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx2
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts3
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts1
20 files changed, 284 insertions, 378 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index b6a59118a..f22df0da2 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -482,20 +482,30 @@ const easeInOutQuad = (currentTime: number, start: number, change: number, durat
return (-change / 2) * (newCurrentTime * (newCurrentTime - 2) - 1) + start;
};
-export function smoothScroll(duration: number, element: HTMLElement, to: number, finish?: () => void) {
- const start = element.scrollTop;
- const change = to - start;
- const startDate = new Date().getTime();
+export function smoothScroll(duration: number, element: HTMLElement | HTMLElement[], to: number, finish?: () => void, reset?: { resetGoTo: { to: number, duration: number } | undefined }) {
+ const elements = (element instanceof HTMLElement ? [element] : element);
+ let starts = elements.map(element => element.scrollTop);
+ let startDate = new Date().getTime();
const animateScroll = () => {
const currentDate = new Date().getTime();
- const currentTime = currentDate - startDate;
- element.scrollTop = easeInOutQuad(currentTime, start, change, duration);
+ let currentTime = currentDate - startDate;
+ const resetParams = reset?.resetGoTo;
+ if (resetParams) {
+ reset!.resetGoTo = undefined;
+ const { to: newTo, duration: newDuration } = resetParams;
+ to = newTo;
+ starts = starts.map(start => easeInOutQuad(currentTime, start, to - start, duration));
+ startDate = currentDate;
+ duration = newDuration;
+ currentTime = currentDate - startDate;
+ }
+ elements.map((element, i) => element.scrollTop = easeInOutQuad(currentTime, starts[i], to - starts[i], duration));
if (currentTime < duration) {
requestAnimationFrame(animateScroll);
} else {
- element.scrollTop = to;
+ elements.forEach(element => element.scrollTop = to);
finish?.();
}
};
diff --git a/src/client/views/AntimodeMenu.scss b/src/client/views/AntimodeMenu.scss
index 6eeb83ba9..c9b5e7658 100644
--- a/src/client/views/AntimodeMenu.scss
+++ b/src/client/views/AntimodeMenu.scss
@@ -8,7 +8,7 @@
background: #323232;
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
border-radius: 0px 6px 6px 6px;
- // overflow: hidden;
+ z-index: 1001;
display: flex;
&.with-rows {
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index de8a3c909..db2d56aa8 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -22,7 +22,7 @@ $linkGap : 3px;
background: none;
}
- .documentDecorations-levelSelector {
+ .documentDecorations-selectorButton {
pointer-events: auto;
height: 15px;
width: 15px;
@@ -210,37 +210,30 @@ $linkGap : 3px;
grid-column-start: 4;
grid-column-end: 4;
pointer-events: all;
- text-align: center;
right: 0;
- top: 0;
cursor: pointer;
position: absolute;
- background: transparent;
width: 20px;
}
-.documentDecorations-openInTab {
+.documentDecorations-openButton {
+ display: flex;
+ align-items: center;
opacity: 1;
grid-column-start: 5;
grid-column-end: 5;
pointer-events: all;
- text-align: center;
cursor: pointer;
}
.documentDecorations-closeButton {
+ display: flex;
+ align-items: center;
opacity: 1;
grid-column-start: 1;
grid-column-end: 3;
pointer-events: all;
- text-align: center;
cursor: pointer;
- position: absolute;
- left: 0px;
- top: 0px;
- width: 8px;
- height: $MINIMIZED_ICON_SIZE;
- max-height: 20px;
>svg {
margin: 0;
@@ -343,12 +336,6 @@ $linkGap : 3px;
.documentdecorations-icon {
margin-top: 3px;
}
-
-.documentdecorations-times {
- margin-top: 3px;
- padding-right: 3px;
-}
-
.templating-button,
.docDecs-tagButton {
width: 20px;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 1e3c0ba92..44d4460fa 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -10,7 +10,7 @@ import { InkField } from "../../fields/InkField";
import { ScriptField } from '../../fields/ScriptField';
import { Cast, NumCast } from "../../fields/Types";
import { GetEffectiveAcl } from '../../fields/util';
-import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../Utils";
+import { setupMoveUpEvents, emptyFunction } from "../../Utils";
import { Docs, DocUtils } from "../documents/Documents";
import { DocumentType } from '../documents/DocumentTypes';
import { CurrentUserUtils } from '../util/CurrentUserUtils';
@@ -26,7 +26,6 @@ import { InkStrokeProperties } from './InkStrokeProperties';
import { LightboxView } from './LightboxView';
import { DocumentView } from "./nodes/DocumentView";
import React = require("react");
-import e = require('express');
@observer
export class DocumentDecorations extends React.Component<{ boundsLeft: number, boundsTop: number }, { value: string }> {
@@ -40,10 +39,10 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
private _rotateUndo?: UndoManager.Batch;
private _offX = 0; _offY = 0; // offset from click pt to inner edge of resize border
private _snapX = 0; _snapY = 0; // last snapped location of resize border
- private _prevX = 0;
private _prevY = 0;
- private _centerPoints: { X: number, Y: number }[] = [];
- private _inkDocs: { x: number, y: number, width: number, height: number }[] = [];
+ private _dragHeights = new Map<Doc, { start: number, lowest: number }>();
+ private _inkCenterPts: { doc: Doc, X: number, Y: number }[] = [];
+ private _inkDragDocs: { doc: Doc, x: number, y: number, width: number, height: number }[] = [];
@observable private _accumulatedTitle = "";
@observable private _titleControlString: string = "#title";
@@ -58,12 +57,12 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
constructor(props: any) {
super(props);
DocumentDecorations.Instance = this;
- reaction(() => SelectionManager.Views().slice(), docs => this.titleBlur(false));
+ reaction(() => SelectionManager.Views().slice(), action(docs => this._edtingTitle = false));
}
@computed
- get Bounds(): { x: number, y: number, b: number, r: number } {
- const boudns = SelectionManager.Views().map(dv => dv.getBounds()).reduce((bounds, rect) =>
+ get Bounds() {
+ return SelectionManager.Views().map(dv => dv.getBounds()).reduce((bounds, rect) =>
!rect ? bounds :
{
x: Math.min(rect.left, bounds.x),
@@ -72,51 +71,34 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
b: Math.max(rect.bottom, bounds.b)
},
{ x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE });
- return boudns;
}
- titleBlur = action((commit: boolean) => {
+ @action
+ titleBlur = () => {
this._edtingTitle = false;
- if (commit) {
- if (this._accumulatedTitle.startsWith("#") || this._accumulatedTitle.startsWith("=")) {
- this._titleControlString = this._accumulatedTitle;
- } else if (this._titleControlString.startsWith("#")) {
- const selectionTitleFieldKey = this._titleControlString.substring(1);
- selectionTitleFieldKey === "title" && (SelectionManager.Views()[0].dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-"));
- UndoManager.RunInBatch(() => selectionTitleFieldKey && SelectionManager.Views().forEach(d => {
- const value = typeof d.props.Document[selectionTitleFieldKey] === "number" ? +this._accumulatedTitle : this._accumulatedTitle;
- Doc.SetInPlace(d.props.Document, selectionTitleFieldKey, value, true);
- }), "title blur");
- }
- }
- });
-
- @action titleEntered = (e: any) => {
- const key = e.keyCode || e.which;
- // enter pressed
- if (key === 13) {
- const text = e.target.value;
- if (text.startsWith("::")) {
- this._accumulatedTitle = text.slice(2, text.length);
- const promoteDoc = SelectionManager.Views()[0];
- Doc.SetInPlace(promoteDoc.props.Document, "title", this._accumulatedTitle, true);
- DocUtils.Publish(promoteDoc.props.Document, this._accumulatedTitle, promoteDoc.props.addDocument, promoteDoc.props.removeDocument);
- }
- e.target.blur();
+ if (this._accumulatedTitle.startsWith("#") || this._accumulatedTitle.startsWith("=")) {
+ this._titleControlString = this._accumulatedTitle;
+ } else if (this._titleControlString.startsWith("#")) {
+ const titleFieldKey = this._titleControlString.substring(1);
+ UndoManager.RunInBatch(() => titleFieldKey && SelectionManager.Views().forEach(d => {
+ titleFieldKey === "title" && (d.dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-"));
+ //@ts-ignore
+ Doc.SetInPlace(d.rootDoc, titleFieldKey, +this._accumulatedTitle == this._accumulatedTitle ? +this._accumulatedTitle : this._accumulatedTitle, true);
+ }), "title blur");
}
}
+
+ titleEntered = (e: React.KeyboardEvent) => e.key === "Enter" && (e.target as any).blur();
+
@action onTitleDown = (e: React.PointerEvent): void => {
- setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), (e) => { }, this.onTitleClick);
- }
- @action onTitleClick = (e: PointerEvent): void => {
- !this._edtingTitle && (this._accumulatedTitle = this._titleControlString.startsWith("#") ? this.selectionTitle : this._titleControlString);
- this._edtingTitle = true;
- setTimeout(() => this._keyinput.current!.focus(), 0);
+ setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), (e) => { }, action((e) => {
+ !this._edtingTitle && (this._accumulatedTitle = this._titleControlString.startsWith("#") ? this.selectionTitle : this._titleControlString);
+ this._edtingTitle = true;
+ setTimeout(() => this._keyinput.current!.focus(), 0);
+ }));
}
- onBackgroundDown = (e: React.PointerEvent): void => {
- setupMoveUpEvents(this, e, e => this.onBackgroundMove(false, e), (e) => { }, (e) => { });
- }
+ onBackgroundDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, e => this.onBackgroundMove(false, e), emptyFunction, emptyFunction);
@action
onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => {
@@ -128,8 +110,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
dragData.isSelectionMove = true;
dragData.canEmbed = dragTitle;
dragData.dropAction = dragDocView.props.dropAction;
- this.Interacting = true;
- this._hidden = true;
+ this._hidden = this.Interacting = true;
DragManager.StartDocumentDrag(SelectionManager.Views().map(dv => dv.ContentDiv!), dragData, e.x, e.y, {
dragComplete: action(e => {
dragData.canEmbed && SelectionManager.DeselectAll();
@@ -140,202 +121,117 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
return true;
}
- onIconifyDown = (e: React.PointerEvent): void => {
- setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onIconifyClick);
- }
@undoBatch
- @action
- onCloseClick = async (e: React.MouseEvent | undefined) => {
- if (!e?.button) {
- const selected = SelectionManager.Views().slice();
- SelectionManager.DeselectAll();
- selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
- }
- }
- @action
- onMaximizeDown = (e: React.PointerEvent): void => {
- setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onMaximizeClick);
+ onCloseClick = () => {
+ const selected = SelectionManager.Views().slice();
+ SelectionManager.DeselectAll();
+ selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
}
@undoBatch
@action
- onMaximizeClick = (e: PointerEvent): void => {
- if (e.button === 0) {
- const selectedDocs = SelectionManager.Views();
- if (selectedDocs.length) {
- if (e.ctrlKey) { // open an alias in a new tab with Ctrl Key
- selectedDocs[0].props.Document._fullScreenView = Doc.MakeAlias(selectedDocs[0].props.Document);
- (selectedDocs[0].props.Document._fullScreenView as Doc).context = undefined;
- CollectionDockingView.AddSplit(selectedDocs[0].props.Document._fullScreenView as Doc, "right");
- } else if (e.shiftKey) { // open centered in a new workspace with Shift Key
- const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
- alias.context = undefined;
- alias.x = -alias[WidthSym]() / 2;
- alias.y = -alias[HeightSym]() / 2;
- CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([alias], { title: "Tab for " + alias.title }), "right");
- } else if (e.altKey) { // open same document in new tab
- CollectionDockingView.ToggleSplit(Cast(selectedDocs[0].props.Document._fullScreenView, Doc, null) || selectedDocs[0].props.Document, "right");
- } else {
- LightboxView.SetLightboxDoc(selectedDocs[0].props.Document, undefined, selectedDocs.slice(1).map(view => view.props.Document));
- }
+ onMaximizeClick = (e: React.MouseEvent): void => {
+ const selectedDocs = SelectionManager.Views();
+ if (selectedDocs.length) {
+ if (e.ctrlKey) { // open an alias in a new tab with Ctrl Key
+ selectedDocs[0].props.Document._fullScreenView = Doc.MakeAlias(selectedDocs[0].props.Document);
+ (selectedDocs[0].props.Document._fullScreenView as Doc).context = undefined;
+ CollectionDockingView.AddSplit(selectedDocs[0].props.Document._fullScreenView as Doc, "right");
+ } else if (e.shiftKey) { // open centered in a new workspace with Shift Key
+ const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
+ alias.context = undefined;
+ alias.x = -alias[WidthSym]() / 2;
+ alias.y = -alias[HeightSym]() / 2;
+ CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([alias], { title: "Tab for " + alias.title }), "right");
+ } else if (e.altKey) { // open same document in new tab
+ CollectionDockingView.ToggleSplit(Cast(selectedDocs[0].props.Document._fullScreenView, Doc, null) || selectedDocs[0].props.Document, "right");
+ } else {
+ LightboxView.SetLightboxDoc(selectedDocs[0].props.Document, undefined, selectedDocs.slice(1).map(view => view.props.Document));
}
}
SelectionManager.DeselectAll();
}
+
@undoBatch
- @action
- onIconifyClick = (e: PointerEvent): void => {
- (e.button === 0) && SelectionManager.Views().forEach(dv => dv?.iconify());
+ onIconifyClick = (e: React.MouseEvent): void => {
+ SelectionManager.Views().forEach(dv => dv?.iconify());
SelectionManager.DeselectAll();
}
- @action
- onSelectorUp = (e: React.PointerEvent): void => {
- setupMoveUpEvents(this, e, returnFalse, emptyFunction, action((e) =>
- SelectionManager.Views()?.[0]?.props.ContainingCollectionView?.props.select(false)));
- }
+ onSelectorClick = (e: React.MouseEvent) => SelectionManager.Views()?.[0]?.props.ContainingCollectionView?.props.select(false);
- @action
onRadiusDown = (e: React.PointerEvent): void => {
- setupMoveUpEvents(this, e, this.onRadiusMove, (e) => this._resizeUndo?.end(), (e) => { });
- if (e.button === 0) {
- this._resizeUndo = UndoManager.StartBatch("DocDecs set radius");
- }
- }
-
- onRadiusMove = (e: PointerEvent, down: number[]): boolean => {
- let dist = Math.sqrt((e.clientX - down[0]) * (e.clientX - down[0]) + (e.clientY - down[1]) * (e.clientY - down[1]));
- dist = dist < 3 ? 0 : dist;
- SelectionManager.Views().map(dv => dv.props.Document).map(doc => doc.layout instanceof Doc ? doc.layout : doc.isTemplateForField ? doc : Doc.GetProto(doc)).
- map(d => d.borderRounding = `${Math.max(0, dist)}px`);
- return false;
+ this._resizeUndo = UndoManager.StartBatch("DocDecs set radius");
+ setupMoveUpEvents(this, e, (e, down) => {
+ const dist = Math.sqrt((e.clientX - down[0]) * (e.clientX - down[0]) + (e.clientY - down[1]) * (e.clientY - down[1]));
+ SelectionManager.Views().map(dv => dv.props.Document).map(doc => doc.layout instanceof Doc ? doc.layout : doc.isTemplateForField ? doc : Doc.GetProto(doc)).
+ map(d => d.borderRounding = `${Math.max(0, dist < 3 ? 0 : dist)}px`);
+ return false;
+ }, (e) => this._resizeUndo?.end(), (e) => { });
}
- @undoBatch
@action
onRotateDown = (e: React.PointerEvent): void => {
this._rotateUndo = UndoManager.StartBatch("rotatedown");
- setupMoveUpEvents(this, e, this.onRotateMove, this.onRotateUp, (e) => { });
- this._prevX = e.clientX;
+ setupMoveUpEvents(this, e, this.onRotateMove, () => this._rotateUndo?.end(), emptyFunction);
this._prevY = e.clientY;
- SelectionManager.Views().forEach(action((element: DocumentView) => {
- const doc = Document(element.rootDoc);
- if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
- const ink = Cast(doc.data, InkField)?.inkData;
- if (ink) {
- const xs = ink.map(p => p.X);
- const ys = ink.map(p => p.Y);
- const left = Math.min(...xs);
- const top = Math.min(...ys);
- const right = Math.max(...xs);
- const bottom = Math.max(...ys);
- // this._centerPoints.push({ X: ((right - left) / 2) + left, Y: ((bottom - top) / 2) + bottom });
- this._centerPoints.push({ X: left, Y: top });
- }
- }
- }));
-
+ this._inkCenterPts = SelectionManager.Views()
+ .filter(dv => dv.rootDoc.type === DocumentType.INK)
+ .map(dv => ({ ink: Cast(dv.rootDoc.data, InkField)?.inkData ?? [{ X: 0, Y: 0 }], doc: dv.rootDoc }))
+ .map(({ ink, doc }) => ({ doc, X: Math.min(...ink.map(p => p.X)), Y: Math.min(...ink.map(p => p.Y)) }));
}
- @undoBatch
@action
onRotateMove = (e: PointerEvent, down: number[]): boolean => {
-
- // const distance = Math.sqrt((this._prevY - e.clientY) * (this._prevY - e.clientY) + (this._prevX - e.clientX) * (this._prevX - e.clientX));
const distance = Math.abs(this._prevY - e.clientY);
- var angle = 0;
- //think of a better condition later...
- // if ((down[0] < e.clientX && this._prevY < e.clientY) || (down[0] > e.clientX && this._prevY > e.clientY)) {
- if (e.clientY > this._prevY) {
- angle = distance * (Math.PI / 180);
- // } else if ((down[0] < e.clientX && this._prevY > e.clientY) || (down[0] > e.clientX && this._prevY <= e.clientY)) {
- } else if (e.clientY < this._prevY) {
- angle = - distance * (Math.PI / 180);
- }
- this._prevX = e.clientX;
+ const angle = e.clientY > this._prevY ? distance * (Math.PI / 180) : e.clientY < this._prevY ? - distance * (Math.PI / 180) : 0;
this._prevY = e.clientY;
- var index = 0;
- SelectionManager.Views().forEach(action((element: DocumentView) => {
- const doc = Document(element.rootDoc);
- if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
- doc.rotation = Number(doc.rotation) + Number(angle);
- const inks = Cast(doc.data, InkField)?.inkData;
- if (inks) {
- const newPoints: { X: number, Y: number }[] = [];
- for (const ink of inks) {
- const newX = Math.cos(angle) * (ink.X - this._centerPoints[index].X) - Math.sin(angle) * (ink.Y - this._centerPoints[index].Y) + this._centerPoints[index].X;
- const newY = Math.sin(angle) * (ink.X - this._centerPoints[index].X) + Math.cos(angle) * (ink.Y - this._centerPoints[index].Y) + this._centerPoints[index].Y;
- newPoints.push({ X: newX, Y: newY });
- }
- Doc.GetProto(doc).data = new InkField(newPoints);
- const xs = newPoints.map(p => p.X);
- const ys = newPoints.map(p => p.Y);
- const left = Math.min(...xs);
- const top = Math.min(...ys);
- const right = Math.max(...xs);
- const bottom = Math.max(...ys);
-
- // doc._height = (bottom - top) * element.props.ScreenToLocalTransform().Scale;
- // doc._width = (right - left) * element.props.ScreenToLocalTransform().Scale;
- doc._height = (bottom - top);
- doc._width = (right - left);
- }
- index++;
- }
- }));
+ this._inkCenterPts.map(({ doc, X, Y }) => ({ doc, X, Y, inkData: Cast(doc.data, InkField)?.inkData }))
+ .forEach(pair => {
+ const newPoints = pair.inkData?.map(ink => ({
+ X: Math.cos(angle) * (ink.X - pair.X) - Math.sin(angle) * (ink.Y - pair.Y) + pair.X,
+ Y: Math.sin(angle) * (ink.X - pair.X) + Math.cos(angle) * (ink.Y - pair.Y) + pair.Y
+ })) || [];
+ Doc.SetInPlace(pair.doc, "data", new InkField(newPoints), true);
+
+ pair.doc._width = ((xs) => (Math.max(...xs) - Math.min(...xs)))(newPoints.map(p => p.X) || [0]);
+ pair.doc._height = ((ys) => (Math.max(...ys) - Math.min(...ys)))(newPoints.map(p => p.Y) || [0]);
+ pair.doc.rotation = NumCast(pair.doc.rotation) + angle;
+ });
return false;
}
- onRotateUp = (e: PointerEvent) => {
- this._centerPoints = [];
- this._rotateUndo?.end();
- this._rotateUndo = undefined;
- }
-
- _dragHeights = new Map<Doc, { start: number, lowest: number }>();
-
@action
onPointerDown = (e: React.PointerEvent): void => {
-
- this._inkDocs = [];
- SelectionManager.Views().forEach(action((element: DocumentView) => {
- const doc = Document(element.rootDoc);
- if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height) {
- this._inkDocs.push({ x: doc.x, y: doc.y, width: doc._width, height: doc._height });
+ DragManager.docsBeingDragged = SelectionManager.Views().map(dv => dv.rootDoc);
+ this._inkDragDocs = DragManager.docsBeingDragged
+ .filter(doc => doc.type === DocumentType.INK)
+ .map(doc => {
if (InkStrokeProperties.Instance?._lock) {
- Doc.SetNativeHeight(doc, doc._height);
- Doc.SetNativeWidth(doc, doc._width);
+ Doc.SetNativeHeight(doc, NumCast(doc._height));
+ Doc.SetNativeWidth(doc, NumCast(doc._width));
}
- }
- }));
-
- setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, (e) => { });
- if (e.button === 0) {
- this._resizeHdlId = e.currentTarget.className;
- const bounds = e.currentTarget.getBoundingClientRect();
- this._offX = this._resizeHdlId.toLowerCase().includes("left") ? bounds.right - e.clientX : bounds.left - e.clientX;
- this._offY = this._resizeHdlId.toLowerCase().includes("top") ? bounds.bottom - e.clientY : bounds.top - e.clientY;
- this.Interacting = true; // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them
- this._resizeUndo = UndoManager.StartBatch("DocDecs resize");
- }
+ return ({ doc, x: NumCast(doc.x), y: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) });
+ });
+
+ setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
+ this.Interacting = true; // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them
+ this._resizeHdlId = e.currentTarget.className;
+ const bounds = e.currentTarget.getBoundingClientRect();
+ this._offX = this._resizeHdlId.toLowerCase().includes("left") ? bounds.right - e.clientX : bounds.left - e.clientX;
+ this._offY = this._resizeHdlId.toLowerCase().includes("top") ? bounds.bottom - e.clientY : bounds.top - e.clientY;
+ this._resizeUndo = UndoManager.StartBatch("DocDecs resize");
this._snapX = e.pageX;
this._snapY = e.pageY;
- DragManager.docsBeingDragged = SelectionManager.Views().map(dv => dv.rootDoc);
- SelectionManager.Views().map(dv =>
- this._dragHeights.set(dv.layoutDoc, { start: NumCast(dv.layoutDoc._height), lowest: NumCast(dv.layoutDoc._height) }));
+ DragManager.docsBeingDragged.forEach(doc => this._dragHeights.set(doc, { start: NumCast(doc._height), lowest: NumCast(doc._height) }));
}
onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
const first = SelectionManager.Views()[0];
let thisPt = { thisX: e.clientX - this._offX, thisY: e.clientY - this._offY };
var fixedAspect = Doc.NativeAspect(first.layoutDoc);
- SelectionManager.Views().forEach(action((element: DocumentView) => {
- const doc = Document(element.rootDoc);
- if (doc.type === DocumentType.INK && doc._width && doc._height && InkStrokeProperties.Instance?._lock) {
- fixedAspect = Doc.NativeHeight(doc);
- }
- }));
-
+ InkStrokeProperties.Instance?._lock && SelectionManager.Views().filter(dv => dv.rootDoc.type === DocumentType.INK)
+ .forEach(dv => fixedAspect = Doc.NativeAspect(dv.rootDoc));
if (fixedAspect && (this._resizeHdlId === "documentDecorations-bottomRightResizer" || this._resizeHdlId === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles
const project = (p: number[], a: number[], b: number[]) => {
@@ -361,11 +257,6 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
let dragBottom = false;
let dragRight = false;
let dX = 0, dY = 0, dW = 0, dH = 0;
- const unfreeze = () =>
- SelectionManager.Views().forEach(action((element: DocumentView) =>
- ((element.rootDoc.type === DocumentType.COMPARISON ||
- (element.rootDoc.type === DocumentType.WEB && Doc.LayoutField(element.rootDoc) instanceof HtmlField))
- && Doc.NativeHeight(element.layoutDoc)) && element.toggleNativeDimensions()));
switch (this._resizeHdlId) {
case "": break;
case "documentDecorations-topLeftResizer":
@@ -380,7 +271,6 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
dH = -move[1];
break;
case "documentDecorations-topResizer":
- unfreeze();
dY = -1;
dH = -move[1];
break;
@@ -394,17 +284,14 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
dH = move[1];
break;
case "documentDecorations-bottomResizer":
- unfreeze();
dH = move[1];
dragBottom = true;
break;
case "documentDecorations-leftResizer":
- unfreeze();
dX = -1;
dW = -move[0];
break;
case "documentDecorations-rightResizer":
- unfreeze();
dW = move[0];
dragRight = true;
break;
@@ -456,6 +343,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
else {
if (dragBottom && (e.ctrlKey || docView.layoutDoc._fitWidth)) { // frozen web pages and others that fitWidth can't grow horizontally to match a vertical resize so the only choice is to change the nativeheight even if the ctrl key isn't used
doc._nativeHeight = actualdH / (doc._height || 1) * Doc.NativeHeight(doc);
+ doc._autoHeight = false;
} else {
if (!doc._fitWidth) doc._width = nwidth / nheight * actualdH;
else if (!e.ctrlKey) doc._width = actualdW;
@@ -463,8 +351,8 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
doc._height = actualdH;
}
} else {
- dW && (doc._width = actualdW);
dH && (doc._height = actualdH);
+ dW && (doc._width = actualdW);
dH && (doc._autoHeight = false);
}
}
@@ -476,41 +364,26 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
@action
onPointerUp = (e: PointerEvent): void => {
- SelectionManager.Views().map(dv => {
- const hgts = this._dragHeights.get(dv.layoutDoc);
- if (hgts && hgts.lowest < hgts.start && hgts.lowest <= 20) {
- dv.effectiveNativeWidth > 0 && Doc.toggleNativeDimensions(dv.layoutDoc, dv.ContentScale(), dv.props.PanelWidth(), dv.props.PanelHeight());
- if (dv.layoutDoc._autoHeight) dv.layoutDoc._autoHeight = false;
- setTimeout(() => dv.layoutDoc._autoHeight = true);
- }
- });
this._resizeHdlId = "";
this.Interacting = false;
- (e.button === 0) && this._resizeUndo?.end();
- this._resizeUndo = undefined;
+ this._resizeUndo?.end();
SnappingManager.clearSnapLines();
-
+ // detect autoHeight gesture and apply
+ DragManager.docsBeingDragged.map(doc => ({ doc, hgts: this._dragHeights.get(doc) }))
+ .filter(pair => pair.hgts && pair.hgts.lowest < pair.hgts.start && pair.hgts.lowest <= 20)
+ .forEach(pair => pair.doc._autoHeight = true);
//need to change points for resize, or else rotation/control points will fail.
- SelectionManager.Views().forEach(action((element: DocumentView, index) => {
- const doc = Document(element.rootDoc);
- if (doc.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) {
- const ink = Cast(doc.data, InkField)?.inkData;
- if (ink) {
- const newPoints: { X: number, Y: number }[] = [];
- ink.forEach(i => {
- // (new x — oldx) + (oldxpoint * newWidt)/oldWidth
- const newX = ((doc.x || 0) - this._inkDocs[index].x) + (i.X * (doc._width || 0)) / this._inkDocs[index].width;
- const newY = ((doc.y || 0) - this._inkDocs[index].y) + (i.Y * (doc._height || 0)) / this._inkDocs[index].height;
- newPoints.push({ X: newX, Y: newY });
- });
- Doc.GetProto(doc).data = new InkField(newPoints);
-
- }
- doc._nativeWidth = 0;
- doc._nativeHeight = 0;
- }
- }));
+ this._inkDragDocs.map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
+ .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => {
+ Doc.SetInPlace(doc, "data", new InkField(inkPts.map(ipt => // (new x — oldx) + newWidth * (oldxpoint /oldWidth)
+ ({
+ X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width,
+ Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height
+ }))), true);
+ Doc.SetNativeWidth(doc, undefined);
+ Doc.SetNativeHeight(doc, undefined);
+ });
}
@computed
@@ -524,58 +397,32 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
return Field.toString(selected.props.Document[this._titleControlString.substring(1)] as Field) || "-unset-";
}
return this._accumulatedTitle;
- } else if (SelectionManager.Views().length > 1) {
- return "-multiple-";
- }
- return "-unset-";
- }
-
- TextBar: HTMLDivElement | undefined;
- private setTextBar = (ele: HTMLDivElement) => {
- if (ele) {
- this.TextBar = ele;
}
- }
- public static DocumentIcon(layout: string) {
- const button = layout.indexOf("PDFBox") !== -1 ? "file-pdf" :
- layout.indexOf("ImageBox") !== -1 ? "image" :
- layout.indexOf("Formatted") !== -1 ? "sticky-note" :
- layout.indexOf("Video") !== -1 ? "film" :
- layout.indexOf("Collection") !== -1 ? "object-group" :
- "caret-up";
- return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />;
+ return SelectionManager.Views().length > 1 ? "-multiple-" : "-unset-";
}
render() {
- const darkScheme = CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimgray" : undefined;
const bounds = this.Bounds;
- const seldoc = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+ const seldoc = SelectionManager.Views().lastElement();
if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
return (null);
}
+ const canOpen = SelectionManager.Views().some(docView => !docView.props.Document._stayInCollection);
const canDelete = SelectionManager.Views().some(docView => {
const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
- const docAcl = GetEffectiveAcl(docView.props.Document);
- return (!docView.props.Document._stayInCollection || docView.props.Document.isInkMask) &&
- (collectionAcl === AclAdmin || collectionAcl === AclEdit || docAcl === AclAdmin);
+ return (!docView.rootDoc._stayInCollection || docView.rootDoc.isInkMask) &&
+ (collectionAcl === AclAdmin || collectionAcl === AclEdit || GetEffectiveAcl(docView.rootDoc) === AclAdmin);
});
- const canOpen = SelectionManager.Views().some(docView => !docView.props.Document._stayInCollection);
- const closeIcon = !canDelete ? <div></div> : (
- <Tooltip title={<div className="dash-tooltip">Close</div>} placement="top">
- <div className="documentDecorations-closeButton" onClick={this.onCloseClick}>
- <FontAwesomeIcon className="documentdecorations-times" icon={"times"} size="lg" />
- </div></Tooltip>);
-
- const openIcon = !canOpen ? (null) :
- <Tooltip key="open" title={<div className="dash-tooltip">Open in Tab (ctrl: as alias, shift: in new collection)</div>} placement="top">
- <div className="documentDecorations-openInTab" onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} onPointerDown={this.onMaximizeDown}>
- <FontAwesomeIcon icon="external-link-alt" className="documentView-minimizedIcon" />
+ const topBtn = (key: string, icon: string, click: (e: React.MouseEvent) => void, title: string) => (
+ <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
+ <div className={`documentDecorations-${key}Button`} onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} onClick={click}>
+ <FontAwesomeIcon icon={icon as any} />
</div>
- </Tooltip>;
+ </Tooltip>);
const titleArea = this._edtingTitle ?
<input ref={this._keyinput} className="documentDecorations-title" style={{ width: `calc(100% - ${seldoc?.props.hideResizeHandles ? 0 : 20}px` }} type="text" name="dynbox" autoComplete="on" value={this._accumulatedTitle}
- onBlur={e => this.titleBlur(true)} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} /> :
+ onBlur={e => this.titleBlur()} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} /> :
<div className="documentDecorations-title" style={{ width: `calc(100% - ${seldoc?.props.hideResizeHandles ? 0 : 20}px` }} key="title" onPointerDown={this.onTitleDown} >
<span className="documentDecorations-titleSpan">{`${this.selectionTitle}`}</span>
</div>;
@@ -593,7 +440,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight));
const useRotation = seldoc.rootDoc.type === DocumentType.INK;
- return (<div className="documentDecorations" style={{ background: darkScheme }} >
+ return (<div className="documentDecorations" style={{ background: CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimgray" : "" }} >
<div className="documentDecorations-background" style={{
width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
height: (bounds.b - bounds.y + this._resizeBorderWidth) + "px",
@@ -601,26 +448,21 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
top: bounds.y - this._resizeBorderWidth / 2,
pointerEvents: KeyManager.Instance.ShiftPressed || this.Interacting ? "none" : "all",
display: SelectionManager.Views().length <= 1 ? "none" : undefined
- }} onPointerDown={this.onBackgroundDown} onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} >
- </div>
+ }} onPointerDown={this.onBackgroundDown} onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} />
{bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? (null) : <>
- <div className="documentDecorations-container" key="container" ref={this.setTextBar} style={{
+ <div className="documentDecorations-container" key="container" style={{
width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight) + "px",
left: bounds.x - this._resizeBorderWidth / 2,
top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
}}>
- {closeIcon}
+ {!canDelete ? <div /> : topBtn("close", "times", this.onCloseClick, "Close")}
{seldoc.props.Document.type === DocumentType.EQUATION ? (null) : titleArea}
{seldoc.props.hideResizeHandles || seldoc.props.Document.type === DocumentType.EQUATION ? (null) :
<>
{SelectionManager.Views().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) :
- <Tooltip key="i" title={<div className="dash-tooltip">{`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}</div>} placement="top">
- <div className="documentDecorations-iconifyButton" onPointerDown={this.onIconifyDown}>
- <FontAwesomeIcon icon={seldoc.finalLayoutKey.includes("icon") ? "window-restore" : "window-minimize"} className="documentView-minimizedIcon" />
- </div>
- </Tooltip>}
- {openIcon}
+ topBtn("iconify", `window-${seldoc.finalLayoutKey.includes("icon") ? "restore" : "minimize"}`, this.onIconifyClick, `${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`)}
+ {!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeClick, "Open in Tab (ctrl: as alias, shift: in new collection)")}
<div key="tl" className="documentDecorations-topLeftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
<div key="t" className="documentDecorations-topResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
<div key="tr" className="documentDecorations-topRightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
@@ -632,11 +474,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
<div key="br" className="documentDecorations-bottomRightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
{seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
- <Tooltip key="level" title={<div className="dash-tooltip">tap to select containing document</div>} placement="top">
- <div className="documentDecorations-levelSelector"
- onPointerDown={this.onSelectorUp} onContextMenu={e => e.preventDefault()}>
- <FontAwesomeIcon className="documentdecorations-times" icon={"arrow-alt-circle-up"} size="lg" />
- </div></Tooltip>}
+ topBtn("selector", "arrow-alt-circle-up", this.onSelectorClick, "tap to select containing document")}
</>
}
<div key="rot" className={`documentDecorations-${useRotation ? "rotation" : "borderRadius"}`}
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 33eae97a3..20bba1299 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -264,7 +264,7 @@ export class KeyManager {
const pt = SelectionManager.Views()[0].props.ScreenToLocalTransform().transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2);
const text = `__DashDocId(${pt?.[0] || 0},${pt?.[1] || 0}):` + SelectionManager.Views().map(dv => dv.Document[Id]).join(":");
SelectionManager.Views().length && navigator.clipboard.writeText(text);
- DocumentDecorations.Instance.onCloseClick(undefined);
+ DocumentDecorations.Instance.onCloseClick();
stopPropagation = false;
preventDefault = false;
}
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index ce7f3d8d9..9257ee4e6 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -171,7 +171,7 @@ export class InkStrokeProperties {
SelectionManager.Views().forEach(action(inkView => {
const doc = Document(inkView.rootDoc);
if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
- doc.rotation = Number(doc.rotation) + Number(angle);
+ doc.rotation = NumCast(doc.rotation) + angle;
const ink = Cast(doc.data, InkField)?.inkData;
if (ink) {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index a159a9948..4b6f1c912 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -510,7 +510,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
var index = 0;
if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
- doc.rotation = Number(doc.rotation) + Number(angle);
+ doc.rotation = NumCast(doc.rotation) + angle;
const inks = Cast(doc.data, InkField)?.inkData;
if (inks) {
const newPoints: { X: number, Y: number }[] = [];
@@ -596,7 +596,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
upDownButtons = (dirs: string, field: string) => {
switch (field) {
case "rot": this.rotate((dirs === "up" ? .1 : -.1)); break;
- // case "rot": this.selectedInk?.forEach(i => i.rootDoc.rotation = NumCast(i.rootDoc.rotation) + (dirs === "up" ? 0.1 : -0.1)); break;
case "Xps": this.selectedDoc && (this.selectedDoc.x = NumCast(this.selectedDoc?.x) + (dirs === "up" ? 10 : -10)); break;
case "Yps": this.selectedDoc && (this.selectedDoc.y = NumCast(this.selectedDoc?.y) + (dirs === "up" ? 10 : -10)); break;
case "stk": this.selectedDoc && (this.selectedDoc.strokeWidth = NumCast(this.selectedDoc?.strokeWidth) + (dirs === "up" ? .1 : -.1)); break;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 74f00d1b3..660790b11 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -626,7 +626,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
onClick = (e: React.MouseEvent) => {
if ((Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3)) {
if (e.shiftKey && Date.now() - this._lastTap < 300) { // reset zoom of freeform view to 1-to-1 on a shift + double click
- this.layoutDoc.targetScale && this.zoomSmoothlyAboutPt(this.getTransform().transformPoint(e.clientX, e.clientY), 1);
+ this.zoomSmoothlyAboutPt(this.getTransform().transformPoint(e.clientX, e.clientY), 1);
e.stopPropagation();
e.preventDefault();
}
@@ -815,7 +815,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@action
onPointerWheel = (e: React.WheelEvent): void => {
- if (this.layoutDoc._lockedTransform || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === "outline") return;
+ if (this.layoutDoc._lockedTransform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === "outline") return;
if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
e.stopPropagation();
}
@@ -824,7 +824,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
e.preventDefault();
this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
}
- this.props.Document.targetScale = NumCast(this.props.Document[this.scaleFieldKey]);
}
@action
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d4f6c076a..83022a759 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -759,7 +759,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
contentScaling = () => this.ContentScale;
onClickFunc = () => this.onClickHandler;
- setHeight = (height: number) => this.rootDoc._height = height;
+ setHeight = (height: number) => this.layoutDoc._height = height;
setContentView = (view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view;
@observable contentsActive: () => boolean = returnFalse;
@action setContentsActive = (setActive: () => boolean) => this.contentsActive = setActive;
@@ -1019,7 +1019,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
@computed get nativeScaling() {
if (this.shouldNotScale()) return 1;
const minTextScale = this.Document.type === DocumentType.RTF ? 0.1 : 0;
- if (this.props.PanelHeight() / this.effectiveNativeHeight > this.props.PanelWidth() / this.effectiveNativeWidth) {
+ if (this.layoutDoc._fitWidth || this.props.PanelHeight() / this.effectiveNativeHeight > this.props.PanelWidth() / this.effectiveNativeWidth) {
return Math.max(minTextScale, this.props.PanelWidth() / this.effectiveNativeWidth); // width-limited or fitWidth
}
return Math.max(minTextScale, this.props.PanelHeight() / this.effectiveNativeHeight); // height-limited or unscaled
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index f49405d02..9111cb418 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -32,6 +32,12 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps, EquationDo
this._ref.current!.mathField.focus();
this._ref.current!.mathField.select();
}
+ reaction(() => StrCast(this.dataDoc.text),
+ text => {
+ if (text && text !== this._ref.current!.mathField.latex()) {
+ this._ref.current!.mathField.latex(text);
+ }
+ });
reaction(() => this.props.isSelected(),
selected => {
if (this._ref.current) {
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index f35677014..e8bec9676 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -33,7 +33,7 @@ export class FunctionPlotBox extends ViewBoxBaseComponent<FieldViewProps, Equati
}
componentDidMount() {
this.props.setContentView?.(this);
- reaction(() => [DocListCast(this.dataDoc.data).lastElement()?.text, this.dataDoc.xRange, this.dataDoc.yRange],
+ reaction(() => [DocListCast(this.dataDoc.data).lastElement()?.text, this.layoutDoc.width, this.layoutDoc.height, this.dataDoc.xRange, this.dataDoc.yRange],
() => this.createGraph());
}
getAnchor = () => {
@@ -57,7 +57,6 @@ export class FunctionPlotBox extends ViewBoxBaseComponent<FieldViewProps, Equati
const width = this.props.PanelWidth();
const height = this.props.PanelHeight();
const fn = StrCast(DocListCast(this.dataDoc.data).lastElement()?.text, "x^2").replace(/\\frac\{(.*)\}\{(.*)\}/, "($1/$2)");
- console.log("Graphing:" + fn);
try {
this._plot = functionPlot({
target: "#" + this._plotEle.id,
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 6d60c70bf..2536dbe16 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -101,7 +101,7 @@
margin: 0 auto;
display: flex;
height: 100%;
- overflow: hidden;
+ overflow: auto;
.imageBox-fadeBlocker {
width: 100%;
diff --git a/src/client/views/nodes/LinkDocPreview.scss b/src/client/views/nodes/LinkDocPreview.scss
index b7aeaa072..06ae466f0 100644
--- a/src/client/views/nodes/LinkDocPreview.scss
+++ b/src/client/views/nodes/LinkDocPreview.scss
@@ -10,7 +10,6 @@
z-index: 2004;
.linkDocPreview-inner {
background-color: white;
- border: 8px solid white;
width: 100%;
height: 100%;
pointer-events: none;
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 716810536..3fe164f8a 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -109,8 +109,20 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _width: 200, _height: 400, useCors: true }), "add:right");
}
}
- width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225));
- height = () => Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225));
+ width = () => {
+ if (!this._targetDoc) return 225;
+ if (this._targetDoc[WidthSym]() < this._targetDoc?.[HeightSym]()) {
+ return Math.min(225, this._targetDoc[HeightSym]()) * this._targetDoc[WidthSym]() / this._targetDoc[HeightSym]();
+ }
+ return Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225));
+ }
+ height = () => {
+ if (!this._targetDoc) return 225;
+ if (this._targetDoc[WidthSym]() > this._targetDoc?.[HeightSym]()) {
+ return Math.min(225, this._targetDoc[WidthSym]()) * this._targetDoc[HeightSym]() / this._targetDoc[WidthSym]();
+ }
+ return Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225));
+ }
@computed get previewHeader() {
return !this._linkDoc || !this._targetDoc || !this._linkSrc ? (null) :
<div className="linkDocPreview-info" ref={this._infoRef}>
@@ -177,8 +189,9 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
}
render() {
+ const borders = 16; // 8px border on each side
return <div className="linkDocPreview" onPointerDown={this.followLink}
- style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + 16 }}>
+ style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
{this.docPreview}
</div>;
}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 33147e7f3..c0e0296fe 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -6,7 +6,7 @@ import "pdfjs-dist/web/pdf_viewer.css";
import { Doc, Opt, WidthSym } from "../../../fields/Doc";
import { documentSchema } from '../../../fields/documentSchemas';
import { makeInterface } from "../../../fields/Schema";
-import { Cast, NumCast } from "../../../fields/Types";
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { PdfField } from "../../../fields/URLField";
import { TraceMobx } from '../../../fields/util';
import { Utils, returnOne } from '../../../Utils';
@@ -23,6 +23,7 @@ import { pageSchema } from "./ImageBox";
import "./PDFBox.scss";
import React = require("react");
import { DocAfterFocusFunc } from './DocumentView';
+import { Docs } from '../../documents/Documents';
type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
@@ -85,7 +86,17 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
this.initialScrollTarget = doc;
return this._pdfViewer?.scrollFocus(doc, smooth);
}
- getAnchor = () => this.rootDoc;
+ getAnchor = () => {
+ const anchor = Docs.Create.TextanchorDocument({
+ title: StrCast(this.rootDoc.title + " " + this.layoutDoc._scrollTop),
+ useLinkSmallAnchor: true,
+ hideLinkButton: true,
+ annotationOn: this.rootDoc,
+ y: NumCast(this.layoutDoc._scrollTop),
+ });
+ this.addDocument(anchor);
+ return anchor;
+ }
componentWillUnmount() { this._selectReactionDisposer?.(); }
componentDidMount() {
this.props.setContentView?.(this);
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 0cf052501..156fe64c9 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -26,11 +26,11 @@ import { ViewBoxAnnotatableComponent } from "../DocComponent";
import { DocumentDecorations } from "../DocumentDecorations";
import { MarqueeAnnotator } from "../MarqueeAnnotator";
import { Annotation } from "../pdf/Annotation";
-import { DocAfterFocusFunc } from "./DocumentView";
import { FieldView, FieldViewProps } from './FieldView';
+import { LinkDocPreview } from "./LinkDocPreview";
import "./WebBox.scss";
+import { DocumentType } from '../../documents/DocumentTypes';
import React = require("react");
-import { LinkDocPreview } from "./LinkDocPreview";
const htmlToText = require("html-to-text");
type WebDocument = makeInterface<[typeof documentSchema]>;
@@ -47,7 +47,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
private _iframeIndicatorRef = React.createRef<HTMLDivElement>();
private _iframeDragRef = React.createRef<HTMLDivElement>();
private _keyInput = React.createRef<HTMLInputElement>();
- private _ignoreScroll = false;
+ private _ignoreScroll = "";
+ private _scrollTimer: any;
private _initialScroll: Opt<number>;
@observable private _marqueeing: number[] | undefined;
@observable private _url: string = "hello";
@@ -90,41 +91,63 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
}
})));
iframe.contentDocument.addEventListener('wheel', this.iframeWheel, false);
+ iframe.contentDocument.addEventListener('scroll', this.iframeScroll, false);
}
}
- @action
- onWheelScroll = (scrollTop: number) => {
- if (this.webpage && this._outerRef.current) {
- this.webpage.scrollLeft = 0;
- this._outerRef.current.scrollTop = scrollTop;
- this._outerRef.current.scrollLeft = 0;
- this._ignoreScroll = true;
- if (this.layoutDoc._scrollTop !== scrollTop) {
- this.layoutDoc._scrollTop = scrollTop;
- }
- this._ignoreScroll = false;
- }
+ resetIgnoreScroll = () => {
+ this._scrollTimer && clearTimeout(this._scrollTimer);
+ this._scrollTimer = setTimeout(() => {
+ this._scrollTimer = undefined;
+ this._ignoreScroll = "";
+ }, 250);
+ this._outerRef.current && (this._outerRef.current.scrollLeft = 0);
+ }
+ iframeWheel = (e: any) => {
+ this._ignoreScroll = "iframe";
+ this.resetIgnoreScroll();
+ e.stopPropagation();
+ }
+ onWebWheel = (e: React.WheelEvent) => {
+ this._ignoreScroll = "iframe";
+ this.goTo(Math.max(0, (this.webpage?.scrollTop || 0) + (this._accumulatedGoTo + 1) * e.deltaY), 100);
+ this.resetIgnoreScroll();
+ e.stopPropagation();
}
- iframeWheel = (e: any) => this.webpage && e.target?.children && this.onWheelScroll(this.webpage.scrollTop);
onWheel = (e: React.WheelEvent) => {
- this._outerRef.current && this.onWheelScroll(this._outerRef.current.scrollTop);
+ this._ignoreScroll = "outer";
+ this.resetIgnoreScroll();
e.stopPropagation();
}
-
- getAnchor = () => this.rootDoc;
+ iframeScroll = (e: any) => {
+ if (!this._ignoreScroll.includes("outer") && this._outerRef.current) {
+ this._outerRef.current.scrollTop = this.webpage?.scrollTop || 0;
+ this.layoutDoc._scrollTop = this.webpage?.scrollTop;
+ }
+ }
+ onScroll = (e: any) => {
+ if (!this._ignoreScroll.includes("iframe") && this.webpage) {
+ this.webpage.scrollTop = this._outerRef.current?.scrollTop || 0;
+ this.layoutDoc._scrollTop = this._outerRef.current?.scrollTop;
+ }
+ }
scrollFocus = (doc: Doc, smooth: boolean) => {
let focusSpeed: Opt<number>;
if (doc !== this.rootDoc && this.webpage && this._outerRef.current) {
- const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), this.props.PanelHeight() / (this.props.scaling?.() || 1));
+ const scrollTo = doc.type === DocumentType.TEXTANCHOR ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), this.props.PanelHeight() / (this.props.scaling?.() || 1));
if (scrollTo !== undefined) {
this._initialScroll !== undefined && (this._initialScroll = scrollTo);
- this._ignoreScroll = true;
- this.goTo(scrollTo, focusSpeed = smooth ? 500 : 0);
if (!LinkDocPreview.LinkInfo) {
+ this._ignoreScroll = "iframe|outer";
this.layoutDoc._scrollTop = scrollTo;
+ this._ignoreScroll = "";
}
- this._ignoreScroll = false;
+ this._ignoreScroll = "iframe|outer";
+ this.goTo(scrollTo, focusSpeed = smooth ? 500 : 0);
+ setTimeout(() => {
+ this._scrollTimer = undefined;
+ this._ignoreScroll = "";
+ }, focusSpeed);
}
} else {
this._initialScroll = NumCast(doc.y);
@@ -133,6 +156,18 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
return focusSpeed;
}
+ getAnchor = () => {
+ const anchor = Docs.Create.TextanchorDocument({
+ title: StrCast(this.rootDoc.title + " " + this.layoutDoc._scrollTop),
+ useLinkSmallAnchor: true,
+ hideLinkButton: true,
+ annotationOn: this.rootDoc,
+ y: NumCast(this.layoutDoc._scrollTop),
+ });
+ this.addDocument(anchor);
+ return anchor;
+ }
+
async componentDidMount() {
this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
@@ -172,7 +207,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
if (quickScroll) {
this._initialScroll = scrollTop;
}
- else if (!this._ignoreScroll) {
+ else {
const viewTrans = StrCast(this.Document._viewTransition);
const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
const durationSecStr = viewTrans.match(/([0-9.]*)s/);
@@ -185,11 +220,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
quickScroll = false;
}
+ _accumulatedGoTo = 0;
+ _resetGoTo: { resetGoTo: { to: number, duration: number } | undefined } = { resetGoTo: undefined };
goTo = (scrollTop: number, duration: number) => {
if (this._outerRef.current && this.webpage) {
if (duration) {
- smoothScroll(duration, this.webpage as any as HTMLElement, scrollTop);
- smoothScroll(duration, this._outerRef.current, scrollTop);
+ if (this._accumulatedGoTo++) {
+ this._resetGoTo.resetGoTo = { to: scrollTop, duration };
+ } else {
+ smoothScroll(duration, [this.webpage as any as HTMLElement, this._outerRef.current], scrollTop, () => this._accumulatedGoTo = 0, this._resetGoTo);
+ }
} else {
this.webpage.scrollTop = scrollTop;
this._outerRef.current.scrollTop = scrollTop;
@@ -202,6 +242,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
document.removeEventListener("pointerup", this.onLongPressUp);
document.removeEventListener("pointermove", this.onLongPressMove);
this._iframe?.removeEventListener('wheel', this.iframeWheel);
+ this._iframe?.removeEventListener('scroll', this.iframeScroll);
}
@action
@@ -497,17 +538,18 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
return (<div className="webBox" ref={this._mainCont} >
<div className={`webBox-container`}
style={{ pointerEvents: inactiveLayer ? "none" : undefined }}
+ onWheel={this.onWebWheel}
onContextMenu={this.specificContextMenu}>
<base target="_blank" />
{this.content}
<div className={"webBox-outerContent"} ref={this._outerRef}
style={{
width: `${100 / scale}%`, height: `${100 / scale}%`, transform: `scale(${scale})`,
- pointerEvents: this.layoutDoc.isAnnotating && !inactiveLayer ? "all" : "none"
+ pointerEvents: !this.layoutDoc.isAnnotating || inactiveLayer ? "none" : "all"
}}
onWheel={this.onWheel}
onPointerDown={this.onMarqueeDown}
- onScroll={e => e.stopPropagation()}
+ onScroll={this.onScroll}
>
<div className={"webBox-innerContent"} style={{
height: NumCast(this.scrollHeight, 50),
@@ -527,8 +569,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
addDocument={this.addDocument}
select={emptyFunction}
active={this.active}
- whenActiveChanged={this.whenActiveChanged}>
- </CollectionFreeFormView>
+ whenActiveChanged={this.whenActiveChanged} />
</div>
</div>
{this.annotationLayer}
diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx
index eff018635..508500ab6 100644
--- a/src/client/views/nodes/formattedText/EquationView.tsx
+++ b/src/client/views/nodes/formattedText/EquationView.tsx
@@ -15,24 +15,24 @@ export class EquationView {
this._fieldWrapper = document.createElement("div");
this._fieldWrapper.style.width = node.attrs.width;
this._fieldWrapper.style.height = node.attrs.height;
- this._fieldWrapper.style.fontWeight = "bold";
this._fieldWrapper.style.position = "relative";
this._fieldWrapper.style.display = "inline-block";
- this._fieldWrapper.onkeypress = function (e: any) { e.stopPropagation(); };
- this._fieldWrapper.onkeydown = function (e: any) { e.stopPropagation(); };
- this._fieldWrapper.onkeyup = function (e: any) { e.stopPropagation(); };
this._fieldWrapper.onmousedown = function (e: any) { e.stopPropagation(); };
ReactDOM.render(<EquationViewInternal
fieldKey={node.attrs.fieldKey}
width={node.attrs.width}
height={node.attrs.height}
+ setEditor={this.setEditor}
tbox={tbox}
/>, this._fieldWrapper);
(this as any).dom = this._fieldWrapper;
}
+ _editor: EquationEditor | undefined;
+ setEditor = (editor?: EquationEditor) => this._editor = editor;
destroy() { ReactDOM.unmountComponentAtNode(this._fieldWrapper); }
- selectNode() { }
+ selectNode() { this._editor?.mathField.focus(); }
+ deselectNode() { }
}
interface IEquationViewInternal {
@@ -40,6 +40,7 @@ interface IEquationViewInternal {
tbox: FormattedTextBox;
width: number;
height: number;
+ setEditor: (editor: EquationEditor | undefined) => void;
}
@observer
@@ -47,6 +48,7 @@ export class EquationViewInternal extends React.Component<IEquationViewInternal>
_reactionDisposer: IReactionDisposer | undefined;
_textBoxDoc: Doc;
_fieldKey: string;
+ _ref: React.RefObject<EquationEditor> = React.createRef();
constructor(props: IEquationViewInternal) {
super(props);
@@ -55,6 +57,7 @@ export class EquationViewInternal extends React.Component<IEquationViewInternal>
}
componentWillUnmount() { this._reactionDisposer?.(); }
+ componentDidMount() { this.props.setEditor(this._ref.current ?? undefined); }
render() {
return <div className="equationView" style={{
@@ -62,8 +65,9 @@ export class EquationViewInternal extends React.Component<IEquationViewInternal>
display: "inline-block",
width: this.props.width,
height: this.props.height,
+ bottom: 3,
}}>
- <EquationEditor
+ <EquationEditor ref={this._ref}
value={StrCast(this._textBoxDoc[this._fieldKey], "y=")}
onChange={str => this._textBoxDoc[this._fieldKey] = str}
autoCommands="pi theta sqrt sum prod alpha beta gamma rho"
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 104d60fff..0f669a544 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -554,6 +554,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const uicontrols: ContextMenuProps[] = [];
uicontrols.push({ description: `${FormattedTextBox.CanAnnotate ? "Hide" : "Show"} Annotation Bar`, event: () => FormattedTextBox.CanAnnotate = !FormattedTextBox.CanAnnotate, icon: "expand-arrows-alt" });
+ uicontrols.push({ description: !this.Document._noSidebar ? "Hide Sidebar Handle" : "Show Sidebar Handle", event: () => this.layoutDoc._noSidebar = !this.layoutDoc._noSidebar, icon: "expand-arrows-alt" });
uicontrols.push({ description: `${this.layoutDoc._showAudio ? "Hide" : "Show"} Dictation Icon`, event: () => this.layoutDoc._showAudio = !this.layoutDoc._showAudio, icon: "expand-arrows-alt" });
uicontrols.push({ description: "Show Highlights...", noexpand: true, subitems: highlighting, icon: "hand-point-right" });
!Doc.UserDoc().noviceMode && uicontrols.push({
@@ -597,7 +598,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const options = cm.findByDescription("Options...");
const optionItems = options && "subitems" in options ? options.subitems : [];
- optionItems.push({ description: !this.Document._noSidebar ? "Hide Sidebar Handle" : "Show Sidebar Handle", event: () => this.layoutDoc._noSidebar = !this.layoutDoc._noSidebar, icon: "expand-arrows-alt" });
optionItems.push({ description: !this.Document._singleLine ? "Make Single Line" : "Make Multi Line", event: () => this.layoutDoc._singleLine = !this.layoutDoc._singleLine, icon: "expand-arrows-alt" });
optionItems.push({ description: `${this.Document._autoHeight ? "Lock" : "Auto"} Height`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
!options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index e5d924f42..4b9b78211 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -316,8 +316,7 @@ export class RichTextRules {
if (!fieldKey && !docid) return state.tr;
docid && DocServer.GetRefField(docid).then(docx => {
if (!(docx instanceof Doc && docx)) {
- const docx = Docs.Create.FreeformDocument([], { title: rawdocid, _width: 500, _height: 500 }, docid);
- DocUtils.Publish(docx, docid, returnFalse, returnFalse);
+ Docs.Create.FreeformDocument([], { title: rawdocid, _width: 500, _height: 500 }, docid);
}
});
const node = (state.doc.resolve(start) as any).nodeAfter;
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 3bd41fa7d..9893da3bb 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -247,6 +247,7 @@ export const nodes: { [index: string]: NodeSpec } = {
attrs: {
fieldKey: { default: "" },
},
+ atom: true,
group: "inline",
draggable: false,
toDOM(node) {