aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx208
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx5
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx384
-rw-r--r--src/client/views/nodes/DocumentView.scss29
-rw-r--r--src/client/views/nodes/DocumentView.tsx180
-rw-r--r--src/client/views/nodes/EquationBox.tsx100
-rw-r--r--src/client/views/nodes/FieldView.tsx54
-rw-r--r--src/client/views/nodes/FilterBox.tsx2
-rw-r--r--src/client/views/nodes/FunctionPlotBox.tsx76
-rw-r--r--src/client/views/nodes/ImageBox.tsx10
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx2
-rw-r--r--src/client/views/nodes/LinkDescriptionPopup.tsx74
-rw-r--r--src/client/views/nodes/LinkDocPreview.scss18
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx83
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx95
-rw-r--r--src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx7
-rw-r--r--src/client/views/nodes/PDFBox.tsx83
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx4
-rw-r--r--src/client/views/nodes/ScriptingBox.tsx85
-rw-r--r--src/client/views/nodes/VideoBox.tsx29
-rw-r--r--src/client/views/nodes/WebBox.tsx108
-rw-r--r--src/client/views/nodes/button/FontIconBox.scss34
-rw-r--r--src/client/views/nodes/button/FontIconBox.tsx35
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx30
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx14
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss553
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx248
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx15
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx25
-rw-r--r--src/client/views/nodes/trails/PresElementBox.tsx14
30 files changed, 1598 insertions, 1006 deletions
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 284584a3d..e154e8445 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,26 +1,26 @@
-import { action, computed, observable, trace } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, Opt } from "../../../fields/Doc";
-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 { DashColor, numberRange, OmitKeys } from "../../../Utils";
-import { DocumentManager } from "../../util/DocumentManager";
-import { SelectionManager } from "../../util/SelectionManager";
-import { Transform } from "../../util/Transform";
-import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
-import { DocComponent } from "../DocComponent";
-import { InkingStroke } from "../InkingStroke";
-import { StyleProp } from "../StyleProvider";
-import "./CollectionFreeFormDocumentView.scss";
-import { DocumentView, DocumentViewProps } from "./DocumentView";
-import React = require("react");
+import { action, computed, observable, trace } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, Opt } from '../../../fields/Doc';
+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 { DashColor, numberRange, OmitKeys } from '../../../Utils';
+import { DocumentManager } from '../../util/DocumentManager';
+import { SelectionManager } from '../../util/SelectionManager';
+import { Transform } from '../../util/Transform';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
+import { DocComponent } from '../DocComponent';
+import { InkingStroke } from '../InkingStroke';
+import { StyleProp } from '../StyleProvider';
+import './CollectionFreeFormDocumentView.scss';
+import { DocumentView, DocumentViewProps } from './DocumentView';
+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;
- sizeProvider?: (doc: Doc, replica: string) => { width: number, height: number } | undefined;
+ dataProvider?: (doc: Doc, replica: string) => { x: number; y: number; zIndex?: number; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined;
+ sizeProvider?: (doc: Doc, replica: string) => { width: number; height: number } | undefined;
renderCutoffProvider: (doc: Doc) => boolean;
zIndex?: number;
highlight?: boolean;
@@ -32,29 +32,51 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
@observer
export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps>() {
- public static animFields = ["_height", "_width", "x", "y", "_scrollTop", "opacity"]; // fields that are configured to be animatable using animation frames
+ public static animFields = ['_height', '_width', 'x', 'y', '_scrollTop', 'opacity']; // fields that are configured to be animatable using animation frames
@observable _animPos: number[] | undefined = undefined;
@observable _contentView: DocumentView | undefined | null;
- get displayName() { return "CollectionFreeFormDocumentView(" + this.rootDoc.title + ")"; } // this makes mobx trace() statements more descriptive
- get maskCentering() { return this.props.Document.isInkMask ? InkingStroke.MaskDim / 2 : 0; }
- get transform() { return `translate(${this.X - this.maskCentering}px, ${this.Y - this.maskCentering}px) rotate(${NumCast(this.Document.jitterRotation, this.props.jitterRotation)}deg)`; }
- get X() { return this.dataProvider ? this.dataProvider.x : NumCast(this.Document.x); }
- get Y() { return this.dataProvider ? this.dataProvider.y : NumCast(this.Document.y); }
- get ZInd() { return this.dataProvider ? this.dataProvider.zIndex : NumCast(this.Document.zIndex); }
- get Opacity() { return this.dataProvider ? this.dataProvider.opacity : undefined; }
- get Highlight() { return this.dataProvider?.highlight; }
- @computed get ShowTitle() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as (Opt<string>); }
- @computed get dataProvider() { return this.props.dataProvider?.(this.props.Document, this.props.replica); }
- @computed get sizeProvider() { return this.props.sizeProvider?.(this.props.Document, this.props.replica); }
+ get displayName() {
+ return 'CollectionFreeFormDocumentView(' + this.rootDoc.title + ')';
+ } // this makes mobx trace() statements more descriptive
+ get maskCentering() {
+ return this.props.Document.isInkMask ? InkingStroke.MaskDim / 2 : 0;
+ }
+ get transform() {
+ return `translate(${this.X - this.maskCentering}px, ${this.Y - this.maskCentering}px) rotate(${NumCast(this.Document.jitterRotation, this.props.jitterRotation)}deg)`;
+ }
+ get X() {
+ return this.dataProvider ? this.dataProvider.x : NumCast(this.Document.x);
+ }
+ get Y() {
+ return this.dataProvider ? this.dataProvider.y : NumCast(this.Document.y);
+ }
+ get ZInd() {
+ return this.dataProvider ? this.dataProvider.zIndex : NumCast(this.Document.zIndex);
+ }
+ get Opacity() {
+ return this.dataProvider ? this.dataProvider.opacity : undefined;
+ }
+ get Highlight() {
+ return this.dataProvider?.highlight;
+ }
+ @computed get ShowTitle() {
+ return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as Opt<string>;
+ }
+ @computed get dataProvider() {
+ return this.props.dataProvider?.(this.props.Document, this.props.replica);
+ }
+ @computed get sizeProvider() {
+ return this.props.sizeProvider?.(this.props.Document, this.props.replica);
+ }
styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string) => {
if (property === StyleProp.Opacity && doc === this.layoutDoc) return this.Opacity; // only change the opacity for this specific document, not its children
return this.props.styleProvider?.(doc, props, property);
- }
+ };
public static getValues(doc: Doc, time: 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);
+ 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> });
}
@@ -62,35 +84,43 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static setValues(time: number, d: Doc, vals: { [val: string]: Opt<number> }) {
const timecode = Math.round(time);
Object.keys(vals).forEach(val => {
- const findexed = Cast(d[`${val}-indexed`], listSpec("number"), []).slice();
+ 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 updateKeyframe(docs: Doc[], time: number, targetDoc?: Doc) {
const timecode = Math.round(time);
- 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);
+ docs.forEach(
+ action(doc => {
+ doc._viewTransition = doc.dataTransition = 'all 1s';
+ 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._viewTransition = doc.dataTransition = "all 1s");
- setTimeout(() => docs.forEach(doc => { doc._viewTransition = undefined; 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) {
@@ -102,21 +132,22 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
height.push(NumCast(targDoc._height));
top.push(NumCast(targDoc._height) / -2);
left.push(NumCast(targDoc._width) / -2);
- doc["viewfinder-width-indexed"] = width;
- doc["viewfinder-height-indexed"] = height;
- doc["viewfinder-top-indexed"] = top;
- doc["viewfinder-left-indexed"] = left;
+ doc['viewfinder-width-indexed'] = width;
+ doc['viewfinder-height-indexed'] = height;
+ doc['viewfinder-top-indexed'] = top;
+ doc['viewfinder-left-indexed'] = left;
}
public static setupKeyframes(docs: Doc[], currTimecode: number, makeAppear: boolean = false) {
docs.forEach(doc => {
if (doc.appearFrame === undefined) doc.appearFrame = currTimecode;
- 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
- doc["opacity-indexed"] = new List<number>(numberRange(currTimecode + 1).map(t => !doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1));
+ 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
+ doc['opacity-indexed'] = new List<number>(numberRange(currTimecode + 1).map(t => (!doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1)));
}
- CollectionFreeFormDocumentView.animFields.forEach(val => doc[val] = ComputedField.MakeInterpolated(val, "activeFrame", doc, currTimecode));
- doc.activeFrame = ComputedField.MakeFunction("self.context?._currentFrame||0");
- doc.dataTransition = "inherit";
+ CollectionFreeFormDocumentView.animFields.forEach(val => (doc[val] = ComputedField.MakeInterpolated(val, 'activeFrame', doc, currTimecode)));
+ doc.activeFrame = ComputedField.MakeFunction('self.context?._currentFrame||0');
+ doc.dataTransition = 'inherit';
});
}
@@ -131,7 +162,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
topDoc.x = spt[0];
topDoc.y = spt[1];
this.props.removeDocument?.(topDoc);
- this.props.addDocTab(topDoc, "inParent");
+ this.props.addDocTab(topDoc, 'inParent');
} else {
const spt = this.screenToLocalTransform().inverse().transformPoint(0, 0);
const fpt = screenXf.transformPoint(spt[0], spt[1]);
@@ -141,14 +172,14 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
}
setTimeout(() => SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(topDoc, container)!, false), 0);
}
- }
+ };
nudge = (x: number, y: number) => {
this.props.Document.x = NumCast(this.props.Document.x) + x;
this.props.Document.y = NumCast(this.props.Document.y) + y;
- }
- panelWidth = () => (this.sizeProvider?.width || this.props.PanelWidth?.());
- panelHeight = () => (this.sizeProvider?.height || this.props.PanelHeight?.());
+ };
+ panelWidth = () => this.sizeProvider?.width || this.props.PanelWidth?.();
+ panelHeight = () => this.sizeProvider?.height || this.props.PanelHeight?.();
screenToLocalTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.X, -this.Y);
focusDoc = (doc: Doc) => this.props.focus(doc);
returnThis = () => this;
@@ -163,24 +194,27 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
PanelHeight: this.panelHeight,
};
const background = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
- const mixBlendMode = StrCast(this.layoutDoc.mixBlendMode) as any || (typeof background === "string" && background && (!background.startsWith("linear") && DashColor(background).alpha() !== 1) ? "multiply" : undefined);
- return <div className={"collectionFreeFormDocumentView-container"}
- style={{
- outline: this.Highlight ? "orange solid 2px" : "",
- width: this.panelWidth(),
- height: this.panelHeight(),
- transform: this.transform,
- transformOrigin: '50% 50%',
- transition: this.dataProvider?.transition ?? (this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition)),
- zIndex: this.ZInd,
- mixBlendMode: mixBlendMode,
- display: this.ZInd === -99 ? "none" : undefined
- }} >
- {this.props.renderCutoffProvider(this.props.Document) ?
- <div style={{ position: "absolute", width: this.panelWidth(), height: this.panelHeight(), background: "lightGreen" }} />
- :
- <DocumentView {...divProps} ref={action((r: DocumentView | null) => this._contentView = r)} />
- }
- </div>;
+ const mixBlendMode = (StrCast(this.layoutDoc.mixBlendMode) as any) || (typeof background === 'string' && background && !background.startsWith('linear') && DashColor(background).alpha() !== 1 ? 'multiply' : undefined);
+ return (
+ <div
+ className={'collectionFreeFormDocumentView-container'}
+ style={{
+ outline: this.Highlight ? 'orange solid 2px' : '',
+ width: this.panelWidth(),
+ height: this.panelHeight(),
+ transform: this.transform,
+ transformOrigin: '50% 50%',
+ transition: this.dataProvider?.transition ?? (this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition)),
+ zIndex: this.ZInd,
+ mixBlendMode: mixBlendMode,
+ display: this.ZInd === -99 ? 'none' : undefined,
+ }}>
+ {this.props.renderCutoffProvider(this.props.Document) ? (
+ <div style={{ position: 'absolute', width: this.panelWidth(), height: this.panelHeight(), background: 'lightGreen' }} />
+ ) : (
+ <DocumentView {...divProps} ref={action((r: DocumentView | null) => (this._contentView = r))} />
+ )}
+ </div>
+ );
}
}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index f1d8123da..381436a56 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -118,7 +118,7 @@ export class DocumentContentsView extends React.Component<
FormattedTextBoxProps & {
isSelected: (outsideReaction: boolean) => boolean;
select: (ctrl: boolean) => void;
- scaling?: () => number;
+ NativeDimScaling?: () => number;
setHeight?: (height: number) => void;
layoutKey: string;
}
@@ -161,7 +161,6 @@ export class DocumentContentsView extends React.Component<
'LayoutTemplateString',
'LayoutTemplate',
'dontCenter',
- 'ContentScaling',
'contextMenuItems',
'onClick',
'onDoubleClick',
@@ -195,7 +194,7 @@ export class DocumentContentsView extends React.Component<
// replace HTML<tag> with corresponding HTML tag as in: <HTMLdiv> becomes <HTMLtag Document={props.Document} htmltag='div'>
const replacer2 = (match: any, p1: string, offset: any, string: any) => {
- return `<HTMLtag RootDoc={props.RootDoc} Document={props.Document} scaling='${this.props.scaling?.() || 1}' htmltag='${p1}'`;
+ return `<HTMLtag RootDoc={props.RootDoc} Document={props.Document} scaling='${this.props.NativeDimScaling?.() || 1}' htmltag='${p1}'`;
};
layoutFrame = layoutFrame.replace(/<HTML([a-zA-Z0-9_-]+)/g, replacer2);
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 78d35ab99..a37de7f69 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -1,24 +1,24 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from "@material-ui/core";
-import { action, computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, Opt } from "../../../fields/Doc";
-import { StrCast } from "../../../fields/Types";
-import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils";
-import { DocUtils } from "../../documents/Documents";
-import { DragManager } from "../../util/DragManager";
-import { Hypothesis } from "../../util/HypothesisUtils";
-import { LinkManager } from "../../util/LinkManager";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { Colors } from "../global/globalEnums";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, Opt } from '../../../fields/Doc';
+import { StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
+import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
+import { DocUtils } from '../../documents/Documents';
+import { DragManager } from '../../util/DragManager';
+import { Hypothesis } from '../../util/HypothesisUtils';
+import { LinkManager } from '../../util/LinkManager';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { Colors } from '../global/globalEnums';
import './DocumentLinksButton.scss';
-import { DocumentView } from "./DocumentView";
-import { LinkDescriptionPopup } from "./LinkDescriptionPopup";
-import { TaskCompletionBox } from "./TaskCompletedBox";
-import React = require("react");
+import { DocumentView } from './DocumentView';
+import { LinkDescriptionPopup } from './LinkDescriptionPopup';
+import { TaskCompletionBox } from './TaskCompletedBox';
+import React = require('react');
-const higflyout = require("@hig/flyout");
+const higflyout = require('@hig/flyout');
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -28,7 +28,7 @@ interface DocumentLinksButtonProps {
AlwaysOn?: boolean;
InMenu?: boolean;
StartLink?: boolean; //whether the link HAS been started (i.e. now needs to be completed)
- ContentScaling?: () => number;
+ scaling?: () => number; // how uch doc is scaled so that link buttons can invert it
}
@observer
export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> {
@@ -42,53 +42,71 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
@observable public static invisibleWebDoc: Opt<Doc>;
public static invisibleWebRef = React.createRef<HTMLDivElement>();
- @action @undoBatch
+ @action
+ @undoBatch
onLinkButtonMoved = (e: PointerEvent) => {
if (this.props.InMenu && this.props.StartLink) {
if (this._linkButton.current !== null) {
- const linkDrag = UndoManager.StartBatch("Drag Link");
- this.props.View && DragManager.StartLinkDrag(this._linkButton.current, this.props.View, this.props.View.ComponentView?.getAnchor, e.pageX, e.pageY, {
- dragComplete: dropEv => {
- if (this.props.View && dropEv.linkDocument) {// dropEv.linkDocument equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop
- !dropEv.linkDocument.linkRelationship && (Doc.GetProto(dropEv.linkDocument).linkRelationship = "hyperlink");
- }
- linkDrag?.end();
- },
- hideSource: false
- });
+ const linkDrag = UndoManager.StartBatch('Drag Link');
+ this.props.View &&
+ DragManager.StartLinkDrag(this._linkButton.current, this.props.View, this.props.View.ComponentView?.getAnchor, e.pageX, e.pageY, {
+ dragComplete: dropEv => {
+ if (this.props.View && dropEv.linkDocument) {
+ // dropEv.linkDocument equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop
+ !dropEv.linkDocument.linkRelationship && (Doc.GetProto(dropEv.linkDocument).linkRelationship = 'hyperlink');
+ }
+ linkDrag?.end();
+ },
+ hideSource: false,
+ });
return true;
}
return false;
}
return false;
- }
+ };
onLinkMenuOpen = (e: React.PointerEvent): void => {
- setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => {
- if (doubleTap) {
- DocumentView.showBackLinks(this.props.View.rootDoc);
- }
- }), undefined, undefined,
- action(() => DocumentLinksButton.LinkEditorDocView = this.props.View));
- }
+ setupMoveUpEvents(
+ this,
+ e,
+ this.onLinkButtonMoved,
+ emptyFunction,
+ action((e, doubleTap) => {
+ if (doubleTap) {
+ DocumentView.showBackLinks(this.props.View.rootDoc);
+ }
+ }),
+ undefined,
+ undefined,
+ action(() => (DocumentLinksButton.LinkEditorDocView = this.props.View))
+ );
+ };
@undoBatch
onLinkButtonDown = (e: React.PointerEvent): void => {
- setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => {
- if (doubleTap && this.props.InMenu && this.props.StartLink) {
- //action(() => Doc.BrushDoc(this.props.View.Document));
- if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
- DocumentLinksButton.StartLink = undefined;
- DocumentLinksButton.StartLinkView = undefined;
- } else {
- DocumentLinksButton.StartLink = this.props.View.props.Document;
- DocumentLinksButton.StartLinkView = this.props.View;
+ setupMoveUpEvents(
+ this,
+ e,
+ this.onLinkButtonMoved,
+ emptyFunction,
+ action((e, doubleTap) => {
+ if (doubleTap && this.props.InMenu && this.props.StartLink) {
+ //action(() => Doc.BrushDoc(this.props.View.Document));
+ if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
+ DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
+ } else {
+ DocumentLinksButton.StartLink = this.props.View.props.Document;
+ DocumentLinksButton.StartLinkView = this.props.View;
+ }
}
- }
- }));
- }
+ })
+ );
+ };
- @action @undoBatch
+ @action
+ @undoBatch
onLinkClick = (e: React.MouseEvent): void => {
if (this.props.InMenu && this.props.StartLink) {
DocumentLinksButton.AnnotationId = undefined;
@@ -96,108 +114,125 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
DocumentLinksButton.StartLink = undefined;
DocumentLinksButton.StartLinkView = undefined;
- } else { //if this LinkButton's Document is undefined
+ } else {
+ //if this LinkButton's Document is undefined
DocumentLinksButton.StartLink = this.props.View.props.Document;
DocumentLinksButton.StartLinkView = this.props.View;
}
//action(() => Doc.BrushDoc(this.props.View.Document));
}
- }
-
+ };
completeLink = (e: React.PointerEvent): void => {
- setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action((e, doubleTap) => {
- if (doubleTap && !this.props.StartLink) {
- if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
- DocumentLinksButton.StartLink = undefined;
- DocumentLinksButton.StartLinkView = undefined;
- DocumentLinksButton.AnnotationId = undefined;
- } else if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document) {
- const sourceDoc = DocumentLinksButton.StartLink;
- const targetDoc = this.props.View.ComponentView?.getAnchor?.() || this.props.View.Document;
- const linkDoc = DocUtils.MakeLink({ doc: sourceDoc }, { doc: targetDoc }, "links"); //why is long drag here when this is used for completing links by clicking?
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ emptyFunction,
+ undoBatch(
+ action((e, doubleTap) => {
+ if (doubleTap && !this.props.StartLink) {
+ if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
+ DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
+ DocumentLinksButton.AnnotationId = undefined;
+ } else if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document) {
+ const sourceDoc = DocumentLinksButton.StartLink;
+ const targetDoc = this.props.View.ComponentView?.getAnchor?.() || this.props.View.Document;
+ const linkDoc = DocUtils.MakeLink({ doc: sourceDoc }, { doc: targetDoc }, 'links'); //why is long drag here when this is used for completing links by clicking?
- LinkManager.currentLink = linkDoc;
+ LinkManager.currentLink = linkDoc;
- runInAction(() => {
- if (linkDoc) {
- TaskCompletionBox.textDisplayed = "Link Created";
- TaskCompletionBox.popupX = e.screenX;
- TaskCompletionBox.popupY = e.screenY - 133;
- TaskCompletionBox.taskCompleted = true;
+ runInAction(() => {
+ if (linkDoc) {
+ TaskCompletionBox.textDisplayed = 'Link Created';
+ TaskCompletionBox.popupX = e.screenX;
+ TaskCompletionBox.popupY = e.screenY - 133;
+ TaskCompletionBox.taskCompleted = true;
- LinkDescriptionPopup.popupX = e.screenX;
- LinkDescriptionPopup.popupY = e.screenY - 100;
- LinkDescriptionPopup.descriptionPopup = true;
+ LinkDescriptionPopup.popupX = e.screenX;
+ LinkDescriptionPopup.popupY = e.screenY - 100;
+ LinkDescriptionPopup.descriptionPopup = true;
- const rect = document.body.getBoundingClientRect();
- if (LinkDescriptionPopup.popupX + 200 > rect.width) {
- LinkDescriptionPopup.popupX -= 190;
- TaskCompletionBox.popupX -= 40;
- }
- if (LinkDescriptionPopup.popupY + 100 > rect.height) {
- LinkDescriptionPopup.popupY -= 40;
- TaskCompletionBox.popupY -= 40;
- }
+ const rect = document.body.getBoundingClientRect();
+ if (LinkDescriptionPopup.popupX + 200 > rect.width) {
+ LinkDescriptionPopup.popupX -= 190;
+ TaskCompletionBox.popupX -= 40;
+ }
+ if (LinkDescriptionPopup.popupY + 100 > rect.height) {
+ LinkDescriptionPopup.popupY -= 40;
+ TaskCompletionBox.popupY -= 40;
+ }
- setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500);
+ setTimeout(
+ action(() => (TaskCompletionBox.taskCompleted = false)),
+ 2500
+ );
+ }
+ });
}
- });
- }
- }
- })));
- }
+ }
+ })
+ )
+ );
+ };
- public static finishLinkClick = undoBatch(action((screenX: number, screenY: number, startLink: Doc, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView,) => {
- if (startLink === endLink) {
- DocumentLinksButton.StartLink = undefined;
- DocumentLinksButton.StartLinkView = undefined;
- DocumentLinksButton.AnnotationId = undefined;
- DocumentLinksButton.AnnotationUri = undefined;
- //!this.props.StartLink
- } else if (startLink !== endLink) {
- endLink = endLinkView?.docView?._componentView?.getAnchor?.() || endLink;
- startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.() || startLink;
- const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink },
- DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : undefined, undefined, undefined, true);
+ public static finishLinkClick = undoBatch(
+ action((screenX: number, screenY: number, startLink: Doc, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView) => {
+ if (startLink === endLink) {
+ DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
+ DocumentLinksButton.AnnotationId = undefined;
+ DocumentLinksButton.AnnotationUri = undefined;
+ //!this.props.StartLink
+ } else if (startLink !== endLink) {
+ endLink = endLinkView?.docView?._componentView?.getAnchor?.() || endLink;
+ startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.() || startLink;
+ const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? 'hypothes.is annotation' : undefined, undefined, undefined, true);
- LinkManager.currentLink = linkDoc;
+ LinkManager.currentLink = linkDoc;
- if (DocumentLinksButton.AnnotationId && DocumentLinksButton.AnnotationUri) { // if linking from a Hypothes.is annotation
- Doc.GetProto(linkDoc as Doc).linksToAnnotation = true;
- Doc.GetProto(linkDoc as Doc).annotationId = DocumentLinksButton.AnnotationId;
- Doc.GetProto(linkDoc as Doc).annotationUri = DocumentLinksButton.AnnotationUri;
- const dashHyperlink = Doc.globalServerPath(startIsAnnotation ? endLink : startLink);
- Hypothesis.makeLink(StrCast(startIsAnnotation ? endLink.title : startLink.title), dashHyperlink, DocumentLinksButton.AnnotationId,
- (startIsAnnotation ? startLink : endLink)); // edit annotation to add a Dash hyperlink to the linked doc
- }
+ if (DocumentLinksButton.AnnotationId && DocumentLinksButton.AnnotationUri) {
+ // if linking from a Hypothes.is annotation
+ Doc.GetProto(linkDoc as Doc).linksToAnnotation = true;
+ Doc.GetProto(linkDoc as Doc).annotationId = DocumentLinksButton.AnnotationId;
+ Doc.GetProto(linkDoc as Doc).annotationUri = DocumentLinksButton.AnnotationUri;
+ const dashHyperlink = Doc.globalServerPath(startIsAnnotation ? endLink : startLink);
+ Hypothesis.makeLink(StrCast(startIsAnnotation ? endLink.title : startLink.title), dashHyperlink, DocumentLinksButton.AnnotationId, startIsAnnotation ? startLink : endLink); // edit annotation to add a Dash hyperlink to the linked doc
+ }
- if (linkDoc) {
- TaskCompletionBox.textDisplayed = "Link Created";
- TaskCompletionBox.popupX = screenX;
- TaskCompletionBox.popupY = screenY - 133;
- TaskCompletionBox.taskCompleted = true;
+ if (linkDoc) {
+ TaskCompletionBox.textDisplayed = 'Link Created';
+ TaskCompletionBox.popupX = screenX;
+ TaskCompletionBox.popupY = screenY - 133;
+ TaskCompletionBox.taskCompleted = true;
- if (LinkDescriptionPopup.showDescriptions === "ON" || !LinkDescriptionPopup.showDescriptions) {
- LinkDescriptionPopup.popupX = screenX;
- LinkDescriptionPopup.popupY = screenY - 100;
- LinkDescriptionPopup.descriptionPopup = true;
- }
+ if (LinkDescriptionPopup.showDescriptions === 'ON' || !LinkDescriptionPopup.showDescriptions) {
+ LinkDescriptionPopup.popupX = screenX;
+ LinkDescriptionPopup.popupY = screenY - 100;
+ LinkDescriptionPopup.descriptionPopup = true;
+ }
- const rect = document.body.getBoundingClientRect();
- if (LinkDescriptionPopup.popupX + 200 > rect.width) {
- LinkDescriptionPopup.popupX -= 190;
- TaskCompletionBox.popupX -= 40;
- }
- if (LinkDescriptionPopup.popupY + 100 > rect.height) {
- LinkDescriptionPopup.popupY -= 40;
- TaskCompletionBox.popupY -= 40;
- }
+ const rect = document.body.getBoundingClientRect();
+ if (LinkDescriptionPopup.popupX + 200 > rect.width) {
+ LinkDescriptionPopup.popupX -= 190;
+ TaskCompletionBox.popupX -= 40;
+ }
+ if (LinkDescriptionPopup.popupY + 100 > rect.height) {
+ LinkDescriptionPopup.popupY -= 40;
+ TaskCompletionBox.popupY -= 40;
+ }
- setTimeout(action(() => { TaskCompletionBox.taskCompleted = false; }), 2500);
+ setTimeout(
+ action(() => {
+ TaskCompletionBox.taskCompleted = false;
+ }),
+ 2500
+ );
+ }
}
- }
- }));
+ })
+ );
@action clearLinks() {
DocumentLinksButton.StartLink = undefined;
@@ -208,9 +243,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
const results = [] as Doc[];
const filters = this.props.View.props.docFilters();
Array.from(new Set<Doc>(this.props.View.allLinks)).forEach(link => {
- if (DocUtils.FilterDocs([link], filters, []).length ||
- DocUtils.FilterDocs([link.anchor2 as Doc], filters, []).length ||
- DocUtils.FilterDocs([link.anchor1 as Doc], filters, []).length) {
+ if (DocUtils.FilterDocs([link], filters, []).length || DocUtils.FilterDocs([link.anchor2 as Doc], filters, []).length || DocUtils.FilterDocs([link.anchor1 as Doc], filters, []).length) {
results.push(link);
}
});
@@ -219,48 +252,45 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
/**
* gets the JSX of the link button (btn used to start/complete links) OR the link-view button (btn on bottom left of each linked node)
- *
+ *
* todo:glr / anh seperate functionality such as onClick onPointerDown of link menu button
*/
@computed get linkButtonInner() {
- const btnDim = "30px";
- const link = <img style={{ width: "22px", height: "16px" }} src={`/assets/${"link.png"}`} />;
- const isActive = (DocumentLinksButton.StartLink === this.props.View.props.Document) && this.props.StartLink;
- return (!this.props.InMenu ?
- <div className="documentLinksButton-cont"
- style={{ left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3] }}
- >
- <div className={"documentLinksButton"}
- onPointerDown={this.onLinkMenuOpen} onClick={this.onLinkClick}
+ const btnDim = '30px';
+ const link = <img style={{ width: '22px', height: '16px' }} src={`/assets/${'link.png'}`} />;
+ const isActive = DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.StartLink;
+ return !this.props.InMenu ? (
+ <div className="documentLinksButton-cont" style={{ left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3] }}>
+ <div
+ className={'documentLinksButton'}
+ onPointerDown={this.onLinkMenuOpen}
+ onClick={this.onLinkClick}
style={{
backgroundColor: Colors.LIGHT_BLUE,
color: Colors.BLACK,
- fontSize: "20px",
+ fontSize: '20px',
width: btnDim,
height: btnDim,
}}>
{Array.from(this.filteredLinks).length}
</div>
</div>
- :
- <div className="documentLinksButton-menu" >
- {this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ? //if the origin node is not this node
- <div className={"documentLinksButton-endLink"} ref={this._linkButton}
+ ) : (
+ <div className="documentLinksButton-menu">
+ {this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ? ( //if the origin node is not this node
+ <div
+ className={'documentLinksButton-endLink'}
+ ref={this._linkButton}
onPointerDown={DocumentLinksButton.StartLink && this.completeLink}
onClick={e => DocumentLinksButton.StartLink && DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View)}>
<FontAwesomeIcon className="documentdecorations-icon" icon="link" />
</div>
- : (null)
- }
- {
- this.props.InMenu && this.props.StartLink ? //if link has been started from current node, then set behavior of link button to deactivate linking when clicked again
- <div className={`documentLinksButton ${isActive ? `startLink` : ``}`} ref={this._linkButton}
- onPointerDown={isActive ? undefined : this.onLinkButtonDown} onClick={isActive ? this.clearLinks : this.onLinkClick}>
- <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
- </div>
- :
- (null)
- }
+ ) : null}
+ {this.props.InMenu && this.props.StartLink ? ( //if link has been started from current node, then set behavior of link button to deactivate linking when clicked again
+ <div className={`documentLinksButton ${isActive ? `startLink` : ``}`} ref={this._linkButton} onPointerDown={isActive ? undefined : this.onLinkButtonDown} onClick={isActive ? this.clearLinks : this.onLinkClick}>
+ <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
+ </div>
+ ) : null}
</div>
);
}
@@ -268,25 +298,23 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
render() {
TraceMobx();
- const menuTitle = this.props.StartLink ? "Drag or tap to start link" : "Tap to complete link";
- const buttonTitle = "Tap to view links; double tap to open link collection";
+ const menuTitle = this.props.StartLink ? 'Drag or tap to start link' : 'Tap to complete link';
+ const buttonTitle = 'Tap to view links; double tap to open link collection';
const title = this.props.InMenu ? menuTitle : buttonTitle;
//render circular tooltip if it isn't set to invisible and show the number of doc links the node has, and render inner-menu link button for starting/stopping links if currently in menu
- return (!Array.from(this.filteredLinks).length && !this.props.AlwaysOn) ? (null) :
- <div className="documentLinksButton-wrapper"
+ return !Array.from(this.filteredLinks).length && !this.props.AlwaysOn ? null : (
+ <div
+ className="documentLinksButton-wrapper"
style={{
- transform: this.props.InMenu ? undefined :
- `scale(${(this.props.ContentScaling?.() || 1) * this.props.View.screenToLocalTransform().Scale})`
- }} >
- {
- (this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink)) ||
- (!DocumentLinksButton.LinkEditorDocView && !this.props.InMenu) ?
- <Tooltip title={<div className="dash-tooltip">{title}</div>}>
- {this.linkButtonInner}
- </Tooltip>
- : this.linkButtonInner
- }
- </div>;
+ transform: `scale(${this.props.scaling?.() || 1})`,
+ }}>
+ {(this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink)) || (!DocumentLinksButton.LinkEditorDocView && !this.props.InMenu) ? (
+ <Tooltip title={<div className="dash-tooltip">{title}</div>}>{this.linkButtonInner}</Tooltip>
+ ) : (
+ this.linkButtonInner
+ )}
+ </div>
+ );
}
}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 6a1bfa406..6ea697a2f 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -1,4 +1,4 @@
-@import "../global/globalCssVariables";
+@import '../global/globalCssVariables';
.documentView-effectsWrapper {
border-radius: inherit;
@@ -24,7 +24,7 @@
width: 100%;
height: 100%;
border-radius: inherit;
- transition: outline .3s linear;
+ transition: outline 0.3s linear;
cursor: grab;
// background: $white; //overflow: hidden;
@@ -62,14 +62,16 @@
.documentView-audioBackground {
display: inline-block;
- width: 10%;
+ width: 25px;
height: 25;
position: absolute;
- top: 10px;
- left: 10px;
+ top: 0;
+ left: 50%;
border-radius: 25px;
background: white;
opacity: 0.3;
+ pointer-events: all;
+ cursor: default;
svg {
width: 90% !important;
@@ -88,7 +90,7 @@
width: 100%;
overflow: hidden;
- >.documentView-node {
+ > .documentView-node {
position: absolute;
}
}
@@ -158,7 +160,7 @@
top: 0;
width: 100%;
height: 14;
- background: rgba(0, 0, 0, .4);
+ background: rgba(0, 0, 0, 0.4);
text-align: center;
text-overflow: ellipsis;
white-space: pre;
@@ -187,19 +189,18 @@
transition: opacity 0.5s;
}
}
-
}
.documentView-node:hover,
.documentView-node-topmost:hover {
- >.documentView-styleWrapper {
- >.documentView-titleWrapper-hover {
+ > .documentView-styleWrapper {
+ > .documentView-titleWrapper-hover {
display: inline-block;
}
}
- >.documentView-styleWrapper {
- >.documentView-captionWrapper {
+ > .documentView-styleWrapper {
+ > .documentView-captionWrapper {
opacity: 1;
}
}
@@ -225,6 +226,6 @@
.documentView-node:first-child {
position: relative;
- background: "#B59B66"; //$white;
+ background: '#B59B66'; //$white;
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index dea718a0d..f9ef85595 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -10,7 +10,7 @@ import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
import { listSpec } from '../../../fields/Schema';
import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { AudioField } from '../../../fields/URLField';
import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
import { MobileInterface } from '../../../mobile/MobileInterface';
@@ -52,6 +52,8 @@ import { RadialMenu } from './RadialMenu';
import { ScriptingBox } from './ScriptingBox';
import { PresBox } from './trails/PresBox';
import React = require('react');
+import { DictationManager } from '../../util/DictationManager';
+import { Tooltip } from '@material-ui/core';
const { Howl } = require('howler');
interface Window {
@@ -156,6 +158,7 @@ export interface DocumentViewSharedProps {
scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
createNewFilterDoc?: () => void;
updateFilterDoc?: (doc: Doc) => void;
+ dontHideOnDrag?: boolean;
}
// these props are specific to DocuentViews
@@ -175,9 +178,9 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
LayoutTemplateString?: string;
dontCenter?: 'x' | 'y' | 'xy';
dontScaleFilter?: (doc: Doc) => boolean; // decides whether a document can be scaled to fit its container vs native size with scrolling
- ContentScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal
NativeWidth?: () => number;
NativeHeight?: () => number;
+ NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal NOTE: Must also be added to FieldViewProps
LayoutTemplate?: () => Opt<Doc>;
contextMenuItems?: () => { script: ScriptField; filter?: ScriptField; label: string; icon: string }[];
onClick?: () => ScriptField;
@@ -203,7 +206,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
public static SelectAfterContextMenu = true; // whether a document should be selected after it's contextmenu is triggered.
_animateScaleTime = 300; // milliseconds;
@observable _animateScalingTo = 0;
- @observable _mediaState = 0;
@observable _pendingDoubleClick = false;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _downX: number = 0;
@@ -235,11 +237,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@computed get ShowTitle() {
return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as Opt<string>;
}
- @computed get ContentScale() {
- return this.props.ContentScaling?.() || 1;
+ @computed get NativeDimScaling() {
+ return this.props.NativeDimScaling?.() || 1;
}
@computed get thumb() {
- return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url.href.replace('.png', '_m.png');
+ return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url?.href.replace('.png', '_m.png');
}
@computed get hidden() {
return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Hidden);
@@ -428,7 +430,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
let nheight = Doc.NativeHeight(layoutDoc);
const width = layoutDoc._width || 0;
const height = layoutDoc._height || (nheight / nwidth) * width;
- const scale = this.props.ScreenToLocalTransform().Scale * this.ContentScale;
+ const scale = this.props.ScreenToLocalTransform().Scale * this.NativeDimScaling;
const actualdW = Math.max(width + dW * scale, 20);
const actualdH = Math.max(height + dH * scale, 20);
doc.x = (doc.x || 0) + dX * (actualdW - width);
@@ -491,10 +493,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
startDragging(x: number, y: number, dropAction: dropActionType, hideSource = false) {
if (this._mainCont.current) {
const dragData = new DragManager.DocumentDragData([this.props.Document]);
- const [left, top] = this.props.ScreenToLocalTransform().scale(this.ContentScale).inverse().transformPoint(0, 0);
+ const [left, top] = this.props.ScreenToLocalTransform().scale(this.NativeDimScaling).inverse().transformPoint(0, 0);
dragData.offset = this.props
.ScreenToLocalTransform()
- .scale(this.ContentScale)
+ .scale(this.NativeDimScaling)
.transformDirection(x - left, y - top);
dragData.offset[0] = Math.min(this.rootDoc[WidthSym](), dragData.offset[0]);
dragData.offset[1] = Math.min(this.rootDoc[HeightSym](), dragData.offset[1]);
@@ -502,9 +504,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
dragData.treeViewDoc = this.props.treeViewDoc;
dragData.removeDocument = this.props.removeDocument;
dragData.moveDocument = this.props.moveDocument;
+ //dragData.dimSource :
+ // dragEffects field, set dim
+ // add kv pairs to a doc, swap properties with the node while dragging, and then swap when dropping
+ // add a dragEffects prop to DocumentView as a function that sets up. Each view has its own prop, when you start dragging:
+ // in Draganager, figure out which doc(s) you're dragging and change what opacity function returns
const ffview = this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
ffview && runInAction(() => (ffview.ChildDrag = this.props.DocumentView()));
- DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart) }, () => setTimeout(action(() => ffview && (ffview.ChildDrag = undefined)))); // this needs to happen after the drop event is processed.
+ DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart && !this.props.dontHideOnDrag) }, () =>
+ setTimeout(action(() => ffview && (ffview.ChildDrag = undefined)))
+ ); // this needs to happen after the drop event is processed.
ffview?.setupDragLines(false);
}
}
@@ -716,7 +725,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
this.Document.followLinkLocation = location;
} else if (this.Document._isLinkButton && this.onClickHandler) {
this.Document._isLinkButton = false;
- this.Document['onClick-rawScript'] = this.dataDoc['onClick-rawScript'] = this.dataDoc.onClick = this.Document.onClick = this.layoutDoc.onClick = undefined;
+ this.dataDoc.onClick = this.Document.onClick = this.layoutDoc.onClick = undefined;
}
};
@undoBatch
@@ -750,7 +759,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
};
@undoBatch deleteClicked = () => this.props.removeDocument?.(this.props.Document);
- @undoBatch setToggleDetail = () => (this.Document.onClick = ScriptField.MakeScript(`toggleDetail(documentView, "${StrCast(this.Document.layoutKey).replace('layout_', '')}")`, { documentView: 'any' }));
+ @undoBatch setToggleDetail = () =>
+ (this.Document.onClick = ScriptField.MakeScript(
+ `toggleDetail(documentView, "${StrCast(this.Document.layoutKey)
+ .replace('layout_', '')
+ .replace(/^layout$/, 'detail')}")`,
+ { documentView: 'any' }
+ ));
@undoBatch
@action
@@ -863,7 +878,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
!appearance && cm.addItem({ description: 'UI Controls...', subitems: appearanceItems, icon: 'compass' });
if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
- !Doc.noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? 'Show' : 'Hide'} Audio Button`, event: action(() => (this.layoutDoc._showAudio = !this.layoutDoc._showAudio)), icon: 'microphone' });
const existingOnClick = cm.findByDescription('OnClick...');
const onClicks: ContextMenuProps[] = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
@@ -961,7 +975,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
panelHeight = () => this.props.PanelHeight() - this.headerMargin;
screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
- contentScaling = () => this.ContentScale;
onClickFunc = () => this.onClickHandler;
setHeight = (height: number) => (this.layoutDoc._height = height);
setContentView = action((view: { getAnchor?: () => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
@@ -989,18 +1002,24 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
? true
: false;
};
+ linkButtonInverseScaling = () => (this.props.NativeDimScaling?.() || 1) * this.props.DocumentView().screenToLocalTransform().Scale;
@computed get contents() {
TraceMobx();
- const audioView = !this.layoutDoc._showAudio ? null : (
- <div className="documentView-audioBackground" onPointerDown={this.recordAudioAnnotation} onPointerEnter={this.onPointerEnter}>
- <FontAwesomeIcon
- className="documentView-audioFont"
- style={{ color: [DocListCast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations']).length ? 'blue' : 'gray', 'green', 'red'][this._mediaState] }}
- icon={!DocListCast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations']).length ? 'microphone' : 'file-audio'}
- size="sm"
- />
- </div>
- );
+ const audioAnnosCount = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations'], listSpec(AudioField), null)?.length;
+ const audioTextAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations-text'], listSpec('string'), null);
+ const audioView =
+ (!this.props.isSelected() && !this._isHovering && this.dataDoc.audioAnnoState !== 2) || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() || (!audioAnnosCount && !this.dataDoc.audioAnnoState) ? null : (
+ <Tooltip title={<div>{audioTextAnnos?.lastElement()}</div>}>
+ <div className="documentView-audioBackground" onPointerDown={this.playAnnotation}>
+ <FontAwesomeIcon
+ className="documentView-audioFont"
+ style={{ color: [audioAnnosCount ? 'blue' : 'gray', 'green', 'red'][NumCast(this.dataDoc.audioAnnoState)] }}
+ icon={!audioAnnosCount ? 'microphone' : 'file-audio'}
+ size="sm"
+ />
+ </div>
+ </Tooltip>
+ );
return (
<div
@@ -1041,7 +1060,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
thumbShown={this.thumbShown}
isHovering={this.isHovering}
setContentView={this.setContentView}
- scaling={this.contentScaling}
+ NativeDimScaling={this.props.NativeDimScaling}
PanelHeight={this.panelHeight}
setHeight={!this.props.suppressSetHeight ? this.setHeight : undefined}
isContentActive={this.isContentActive}
@@ -1055,8 +1074,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
{(!this.props.isSelected() && !this._isHovering) || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? null : (
<DocumentLinksButton
View={this.props.DocumentView()}
- ContentScaling={this.props.ContentScaling}
- Offset={[this.topMost ? 0 : !this.props.isSelected() ? -15 : -30, undefined, undefined, this.topMost ? 10 : !this.props.isSelected() ? -15 : -30]}
+ scaling={this.linkButtonInverseScaling}
+ Offset={[this.topMost ? 0 : !this.props.isSelected() ? -15 : -36, undefined, undefined, this.topMost ? 10 : !this.props.isSelected() ? -15 : -28]}
/>
)}
{audioView}
@@ -1134,58 +1153,72 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
@action
- onPointerEnter = () => {
+ playAnnotation = () => {
const self = this;
- const audioAnnos = DocListCast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations']);
- if (audioAnnos && audioAnnos.length && this._mediaState === 0) {
- const anno = audioAnnos[Math.floor(Math.random() * audioAnnos.length)];
- anno.data instanceof AudioField &&
- new Howl({
- src: [anno.data.url.href],
- format: ['mp3'],
- autoplay: true,
- loop: false,
- volume: 0.5,
- onend: function () {
- runInAction(() => (self._mediaState = 0));
- },
- });
- this._mediaState = 1;
+ const audioAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations'], listSpec(AudioField), null);
+ const anno = audioAnnos.lastElement();
+ if (anno instanceof AudioField && this.dataDoc.audioAnnoState === 0) {
+ new Howl({
+ src: [anno.url.href],
+ format: ['mp3'],
+ autoplay: true,
+ loop: false,
+ volume: 0.5,
+ onend: function () {
+ runInAction(() => {
+ self.dataDoc.audioAnnoState = 0;
+ });
+ },
+ });
+ this.dataDoc.audioAnnoState = 1;
}
};
- recordAudioAnnotation = () => {
+
+ static recordAudioAnnotation(dataDoc: Doc, field: string, onEnd?: () => void) {
let gumStream: any;
let recorder: any;
- const self = this;
navigator.mediaDevices
.getUserMedia({
audio: true,
})
.then(function (stream) {
+ let audioTextAnnos = Cast(dataDoc[field + '-audioAnnotations-text'], listSpec('string'), null);
+ if (audioTextAnnos) audioTextAnnos.push('');
+ else audioTextAnnos = dataDoc[field + '-audioAnnotations-text'] = new List<string>(['']);
+ DictationManager.Controls.listen({
+ interimHandler: value => (audioTextAnnos[audioTextAnnos.length - 1] = value),
+ continuous: { indefinite: false },
+ }).then(results => {
+ if (results && [DictationManager.Controls.Infringed].includes(results)) {
+ DictationManager.Controls.stop();
+ }
+ onEnd?.();
+ });
+
gumStream = stream;
recorder = new MediaRecorder(stream);
recorder.ondataavailable = async (e: any) => {
const [{ result }] = await Networking.UploadFilesToServer(e.data);
if (!(result instanceof Error)) {
- const audioDoc = Docs.Create.AudioDocument(result.accessPaths.agnostic.client, { title: 'audio test', _width: 200, _height: 32 });
- audioDoc.treeViewExpandedView = 'layout';
- const audioAnnos = Cast(self.dataDoc[self.LayoutFieldKey + '-audioAnnotations'], listSpec(Doc));
+ const audioField = new AudioField(result.accessPaths.agnostic.client);
+ const audioAnnos = Cast(dataDoc[field + '-audioAnnotations'], listSpec(AudioField), null);
if (audioAnnos === undefined) {
- self.dataDoc[self.LayoutFieldKey + '-audioAnnotations'] = new List([audioDoc]);
+ dataDoc[field + '-audioAnnotations'] = new List([audioField]);
} else {
- audioAnnos.push(audioDoc);
+ audioAnnos.push(audioField);
}
}
};
- runInAction(() => (self._mediaState = 2));
+ runInAction(() => (dataDoc.audioAnnoState = 2));
recorder.start();
setTimeout(() => {
recorder.stop();
- runInAction(() => (self._mediaState = 0));
+ DictationManager.Controls.stop(false);
+ runInAction(() => (dataDoc.audioAnnoState = 0));
gumStream.getAudioTracks()[0].stop();
}, 5000);
});
- };
+ }
captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ':caption');
@computed get innards() {
@@ -1229,6 +1262,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
style={{
position: this.headerMargin ? 'relative' : 'absolute',
height: this.titleHeight,
+ width: !this.headerMargin ? `calc(100% - 18px)` : '100%', // leave room for annotation button
color: lightOrDark(background),
background,
pointerEvents: this.onClickHandler || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
@@ -1283,9 +1317,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
isHovering = () => this._isHovering;
@observable _isHovering = false;
@observable _: string = '';
+ _hoverTimeout: any = undefined;
@computed get renderDoc() {
TraceMobx();
- const thumb = ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url.href.replace('.png', '_m.png');
+ const thumb = ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url?.href.replace('.png', '_m.png');
const isButton = this.props.Document.type === DocumentType.FONTICON;
if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || (this.hidden && !this.props.treeViewDoc)) return null;
return (
@@ -1293,8 +1328,17 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
<div
className={`documentView-node${this.topMost ? '-topmost' : ''}`}
id={this.props.Document[Id]}
- onPointerEnter={action(() => (this._isHovering = true))}
- onPointerLeave={action(() => (this._isHovering = false))}
+ onPointerEnter={action(() => {
+ clearTimeout(this._hoverTimeout);
+ this._isHovering = true;
+ })}
+ onPointerLeave={action(() => {
+ clearTimeout(this._hoverTimeout);
+ this._hoverTimeout = setTimeout(
+ action(() => (this._isHovering = false)),
+ 500
+ );
+ })}
style={{
background: isButton || thumb ? undefined : this.backgroundColor,
opacity: this.opacity,
@@ -1492,7 +1536,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return this.effectiveNativeWidth ? (this.props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2 : 0;
}
@computed get Yshift() {
- return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 && !this.layoutDoc.nativeHeightUnfrozen ? Math.max(0, (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2) : 0;
+ return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 && (!this.layoutDoc.nativeHeightUnfrozen || (!this.fitWidth && this.effectiveNativeHeight * this.nativeScaling <= this.props.PanelHeight()))
+ ? Math.max(0, (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2)
+ : 0;
}
@computed get centeringX() {
return this.props.dontCenter?.includes('x') ? 0 : this.Xshift;
@@ -1501,7 +1547,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return this.props.dontCenter?.includes('y') ? 0 : this.Yshift;
}
- toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.ContentScale, this.props.PanelWidth(), this.props.PanelHeight());
+ toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight());
focus = (doc: Doc, options?: DocFocusOptions) => this.docView?.focus(doc, options);
getBounds = () => {
if (!this.docView || !this.docView.ContentDiv || this.props.Document.presBox || this.docView.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
@@ -1535,11 +1581,15 @@ export class DocumentView extends React.Component<DocumentViewProps> {
Doc.setNativeView(this.props.Document);
custom && DocUtils.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined);
};
- switchViews = action((custom: boolean, view: string, finished?: () => void) => {
+ switchViews = action((custom: boolean, view: string, finished?: () => void, useExistingLayout = false) => {
this.docView && (this.docView._animateScalingTo = 0.1); // shrink doc
setTimeout(
action(() => {
- this.setCustomView(custom, view);
+ if (useExistingLayout && custom && this.rootDoc['layout_' + view]) {
+ this.rootDoc.layoutKey = 'layout_' + view;
+ } else {
+ this.setCustomView(custom, view);
+ }
this.docView && (this.docView._animateScalingTo = 1); // expand it
setTimeout(
action(() => {
@@ -1562,7 +1612,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
NativeHeight = () => this.effectiveNativeHeight;
PanelWidth = () => this.panelWidth;
PanelHeight = () => this.panelHeight;
- ContentScale = () => this.nativeScaling;
+ NativeDimScaling = () => this.nativeScaling;
selfView = () => this;
screenToLocalTransform = () =>
this.props
@@ -1590,8 +1640,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
render() {
TraceMobx();
- const xshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined);
- const yshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined);
+ const xshift = () => (this.props.Document.isInkMask && !this.props.LayoutTemplateString && !this.props.LayoutTemplate?.() ? InkingStroke.MaskDim : Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined);
+ const yshift = () => (this.props.Document.isInkMask && !this.props.LayoutTemplateString && !this.props.LayoutTemplate?.() ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined);
const isPresTreeElement: boolean = this.props.treeViewDoc?.type === DocumentType.PRES;
const isButton: boolean = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear;
return (
@@ -1618,9 +1668,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
PanelHeight={this.PanelHeight}
NativeWidth={this.NativeWidth}
NativeHeight={this.NativeHeight}
+ NativeDimScaling={this.NativeDimScaling}
isSelected={this.isSelected}
select={this.select}
- ContentScaling={this.ContentScale}
ScreenToLocalTransform={this.screenToLocalTransform}
focus={this.props.focus || emptyFunction}
ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))}
@@ -1643,7 +1693,7 @@ ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) {
ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
if (dv.Document.layoutKey === 'layout_' + detailLayoutKeySuffix) dv.switchViews(false, 'layout');
- else dv.switchViews(true, detailLayoutKeySuffix);
+ else dv.switchViews(true, detailLayoutKeySuffix, undefined, true);
});
ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc) {
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index c170f9867..0bd30bce9 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -12,11 +12,12 @@ import { LightboxView } from '../LightboxView';
import './EquationBox.scss';
import { FieldView, FieldViewProps } from './FieldView';
-
@observer
export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(EquationBox, fieldKey); }
- public static SelectOnLoad: string = "";
+ public static LayoutString(fieldKey: string) {
+ return FieldView.LayoutString(EquationBox, fieldKey);
+ }
+ public static SelectOnLoad: string = '';
_ref: React.RefObject<EquationEditor> = React.createRef();
componentDidMount() {
if (EquationBox.SelectOnLoad === this.rootDoc[Id] && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
@@ -25,75 +26,92 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
this._ref.current!.mathField.focus();
this._ref.current!.mathField.select();
}
- reaction(() => StrCast(this.dataDoc.text),
+ reaction(
+ () => StrCast(this.dataDoc.text),
text => {
if (text && text !== this._ref.current!.mathField.latex()) {
this._ref.current!.mathField.latex(text);
}
- });
- reaction(() => this.props.isSelected(),
+ }
+ );
+ reaction(
+ () => this.props.isSelected(),
selected => {
if (this._ref.current) {
- if (selected) this._ref.current.element.current.children[0].addEventListener("keydown", this.keyPressed, true);
- else this._ref.current.element.current.children[0].removeEventListener("keydown", this.keyPressed);
+ if (selected) this._ref.current.element.current.children[0].addEventListener('keydown', this.keyPressed, true);
+ else this._ref.current.element.current.children[0].removeEventListener('keydown', this.keyPressed);
}
- }, { fireImmediately: true });
+ },
+ { fireImmediately: true }
+ );
}
plot: any;
@action
keyPressed = (e: KeyboardEvent) => {
- const _height = Number(getComputedStyle(this._ref.current!.element.current).height.replace("px", ""));
- const _width = Number(getComputedStyle(this._ref.current!.element.current).width.replace("px", ""));
- if (e.key === "Enter") {
+ const _height = Number(getComputedStyle(this._ref.current!.element.current).height.replace('px', ''));
+ const _width = Number(getComputedStyle(this._ref.current!.element.current).width.replace('px', ''));
+ if (e.key === 'Enter') {
const nextEq = Docs.Create.EquationDocument({
- title: "# math", text: StrCast(this.dataDoc.text), _width, _height: 25,
- x: NumCast(this.layoutDoc.x), y: NumCast(this.layoutDoc.y) + _height + 10
+ title: '# math',
+ text: StrCast(this.dataDoc.text),
+ _width,
+ _height: 25,
+ x: NumCast(this.layoutDoc.x),
+ y: NumCast(this.layoutDoc.y) + _height + 10,
});
EquationBox.SelectOnLoad = nextEq[Id];
this.props.addDocument?.(nextEq);
e.stopPropagation();
-
}
- if (e.key === "Tab") {
+ if (e.key === 'Tab') {
const graph = Docs.Create.FunctionPlotDocument([this.rootDoc], {
x: NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym](),
y: NumCast(this.layoutDoc.y),
- _width: 400, _height: 300, backgroundColor: "white"
+ _width: 400,
+ _height: 300,
+ backgroundColor: 'white',
});
this.props.addDocument?.(graph);
e.stopPropagation();
}
- if (e.key === "Backspace" && !this.dataDoc.text) this.props.removeDocument?.(this.rootDoc);
- }
+ if (e.key === 'Backspace' && !this.dataDoc.text) this.props.removeDocument?.(this.rootDoc);
+ };
onChange = (str: string) => {
this.dataDoc.text = str;
const style = this._ref.current && getComputedStyle(this._ref.current.element.current);
if (style) {
- const _height = Number(style.height.replace("px", ""));
- const _width = Number(style.width.replace("px", ""));
+ const _height = Number(style.height.replace('px', ''));
+ const _width = Number(style.width.replace('px', ''));
this.layoutDoc._width = Math.max(35, _width);
this.layoutDoc._height = Math.max(25, _height);
}
- }
+ };
render() {
TraceMobx();
- const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
- return (<div className="equationBox-cont"
- onPointerDown={e => !e.ctrlKey && e.stopPropagation()}
- style={{
- transform: `scale(${scale})`,
- width: `${100 / scale}%`,
- height: `${100 / scale}%`,
- pointerEvents: !this.props.isSelected() ? "none" : undefined,
- }}
- onKeyDown={e => e.stopPropagation()}
- >
- <EquationEditor ref={this._ref}
- value={this.dataDoc.text || "x"}
- spaceBehavesLikeTab={true}
- onChange={this.onChange}
- autoCommands="pi theta sqrt sum prod alpha beta gamma rho"
- autoOperatorNames="sin cos tan" />
- </div>);
+ const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
+ return (
+ <div
+ ref={r => {
+ r instanceof HTMLDivElement &&
+ new ResizeObserver(
+ action((entries: any) => {
+ if (entries[0].contentBoxSize[0].inlineSize) {
+ this.rootDoc._width = entries[0].contentBoxSize[0].inlineSize;
+ }
+ })
+ ).observe(r);
+ }}
+ className="equationBox-cont"
+ onPointerDown={e => !e.ctrlKey && e.stopPropagation()}
+ style={{
+ transform: `scale(${scale})`,
+ width: 'fit-content', // `${100 / scale}%`,
+ height: `${100 / scale}%`,
+ pointerEvents: !this.props.isSelected() ? 'none' : undefined,
+ }}
+ onKeyDown={e => e.stopPropagation()}>
+ <EquationEditor ref={this._ref} value={this.dataDoc.text || 'x'} spaceBehavesLikeTab={true} onChange={this.onChange} autoCommands="pi theta sqrt sum prod alpha beta gamma rho" autoOperatorNames="sin cos tan" />
+ </div>
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 67cf18d8b..dd2c13391 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -1,18 +1,17 @@
-import React = require("react");
-import { computed } from "mobx";
-import { observer } from "mobx-react";
-import { DateField } from "../../../fields/DateField";
-import { Doc, Field, FieldResult, Opt } from "../../../fields/Doc";
-import { List } from "../../../fields/List";
-import { WebField } from "../../../fields/URLField";
-import { DocumentView, DocumentViewSharedProps } from "./DocumentView";
-import { ScriptField } from "../../../fields/ScriptField";
-import { RecordingBox } from "./RecordingBox";
+import React = require('react');
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import { DateField } from '../../../fields/DateField';
+import { Doc, Field, FieldResult, Opt } from '../../../fields/Doc';
+import { List } from '../../../fields/List';
+import { ScriptField } from '../../../fields/ScriptField';
+import { WebField } from '../../../fields/URLField';
+import { DocumentViewSharedProps } from './DocumentView';
//
// these properties get assigned through the render() method of the DocumentView when it creates this node.
// However, that only happens because the properties are "defined" in the markup for the field view.
-// See the LayoutString method on each field view : ImageBox, FormattedTextBox, etc.
+// See the LayoutString method on each field view : ImageBox, FormattedTextBox, etc.
//
export interface FieldViewProps extends DocumentViewSharedProps {
// FieldView specific props that are not part of DocumentView props
@@ -23,10 +22,10 @@ export interface FieldViewProps extends DocumentViewSharedProps {
isContentActive: (outsideReaction?: boolean) => boolean | undefined;
isDocumentActive?: () => boolean;
isSelected: (outsideReaction?: boolean) => boolean;
- scaling?: () => number;
setHeight?: (height: number) => void;
- onBrowseClick?: () => (ScriptField | undefined);
- onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => (boolean | undefined);
+ NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal NOTE: Must also be added to DocumentViewInternalsProps
+ onBrowseClick?: () => ScriptField | undefined;
+ onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => boolean | undefined;
// properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
pointerEvents?: () => Opt<string>;
@@ -42,7 +41,7 @@ export interface FieldViewProps extends DocumentViewSharedProps {
@observer
export class FieldView extends React.Component<FieldViewProps> {
public static LayoutString(fieldType: { name: string }, fieldStr: string) {
- return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`; //e.g., "<ImageBox {...props} fieldKey={"data} />"
+ return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`; //e.g., "<ImageBox {...props} fieldKey={"data} />"
}
@computed
@@ -75,23 +74,22 @@ export class FieldView extends React.Component<FieldViewProps> {
//}
else if (field instanceof DateField) {
return <p>{field.date.toLocaleString()}</p>;
- }
- else if (field instanceof Doc) {
- return <p><b>{field.title?.toString()}</b></p>;
- }
- else if (field instanceof List) {
- return <div> {field.length ? field.map(f => Field.toString(f)).join(", ") : ""} </div>;
+ } else if (field instanceof Doc) {
+ return (
+ <p>
+ <b>{field.title?.toString()}</b>
+ </p>
+ );
+ } else if (field instanceof List) {
+ return <div> {field.length ? field.map(f => Field.toString(f)).join(', ') : ''} </div>;
}
// bcz: this belongs here, but it doesn't render well so taking it out for now
else if (field instanceof WebField) {
return <p>{Field.toString(field.url.href)}</p>;
- }
- else if (!(field instanceof Promise)) {
+ } else if (!(field instanceof Promise)) {
return <p>{Field.toString(field)}</p>;
- }
- else {
- return <p> {"Waiting for server..."} </p>;
+ } else {
+ return <p> {'Waiting for server...'} </p>;
}
}
-
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index ff04a293c..dc3fc0396 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -116,7 +116,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
@observable _loaded = false;
componentDidMount() {
reaction(
- () => DocListCastAsync(this.layoutDoc.data),
+ () => DocListCastAsync(this.layoutDoc[this.fieldKey]),
async activeTabsAsync => {
const activeTabs = await activeTabsAsync;
activeTabs && (await SearchBox.foreachRecursiveDocAsync(activeTabs, emptyFunction));
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index 3ab0a3ff2..15d0f88f6 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -1,4 +1,4 @@
-import functionPlot from "function-plot";
+import functionPlot from 'function-plot';
import { action, computed, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -12,7 +12,6 @@ import { Docs } from '../../documents/Documents';
import { ViewBoxBaseComponent } from '../DocComponent';
import { FieldView, FieldViewProps } from './FieldView';
-
const EquationSchema = createSchema({});
type EquationDocument = makeInterface<[typeof EquationSchema, typeof documentSchema]>;
@@ -20,74 +19,83 @@ const EquationDocument = makeInterface(EquationSchema, documentSchema);
@observer
export class FunctionPlotBox extends ViewBoxBaseComponent<FieldViewProps>() {
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FunctionPlotBox, fieldKey); }
+ public static LayoutString(fieldKey: string) {
+ return FieldView.LayoutString(FunctionPlotBox, fieldKey);
+ }
public static GraphCount = 0;
_plot: any;
- _plotId = "";
+ _plotId = '';
_plotEle: any;
constructor(props: any) {
super(props);
- this._plotId = "graph" + FunctionPlotBox.GraphCount++;
+ this._plotId = 'graph' + FunctionPlotBox.GraphCount++;
}
componentDidMount() {
this.props.setContentView?.(this);
- reaction(() => [DocListCast(this.dataDoc.data).lastElement()?.text, this.layoutDoc.width, this.layoutDoc.height, this.dataDoc.xRange, this.dataDoc.yRange],
- () => this.createGraph());
+ reaction(
+ () => [DocListCast(this.dataDoc[this.fieldKey]).lastElement()?.text, this.layoutDoc.width, this.layoutDoc.height, this.dataDoc.xRange, this.dataDoc.yRange],
+ () => this.createGraph()
+ );
}
getAnchor = () => {
const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc });
anchor.xRange = new List<number>(Array.from(this._plot.options.xAxis.domain));
anchor.yRange = new List<number>(Array.from(this._plot.options.yAxis.domain));
return anchor;
- }
+ };
@action
scrollFocus = (doc: Doc, smooth: boolean) => {
- this.dataDoc.xRange = new List<number>(Array.from(Cast(doc.xRange, listSpec("number"), Cast(this.dataDoc.xRange, listSpec("number"), [-10, 10]))));
- this.dataDoc.yRange = new List<number>(Array.from(Cast(doc.yRange, listSpec("number"), Cast(this.dataDoc.xRange, listSpec("number"), [-1, 9]))));
+ this.dataDoc.xRange = new List<number>(Array.from(Cast(doc.xRange, listSpec('number'), Cast(this.dataDoc.xRange, listSpec('number'), [-10, 10]))));
+ this.dataDoc.yRange = new List<number>(Array.from(Cast(doc.yRange, listSpec('number'), Cast(this.dataDoc.xRange, listSpec('number'), [-1, 9]))));
return 0;
- }
+ };
createGraph = (ele?: HTMLDivElement) => {
this._plotEle = ele || this._plotEle;
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)");
+ const fn = StrCast(DocListCast(this.dataDoc.data).lastElement()?.text, 'x^2').replace(/\\frac\{(.*)\}\{(.*)\}/, '($1/$2)');
try {
this._plot = functionPlot({
- target: "#" + this._plotEle.id,
+ target: '#' + this._plotEle.id,
width,
height,
- xAxis: { domain: Cast(this.dataDoc.xRange, listSpec("number"), [-10, 10]) },
- yAxis: { domain: Cast(this.dataDoc.xRange, listSpec("number"), [-1, 9]) },
+ xAxis: { domain: Cast(this.dataDoc.xRange, listSpec('number'), [-10, 10]) },
+ yAxis: { domain: Cast(this.dataDoc.xRange, listSpec('number'), [-1, 9]) },
grid: true,
data: [
{
fn,
// derivative: { fn: "2 * x", updateOnMouseMove: true }
- }
- ]
+ },
+ ],
});
} catch (e) {
console.log(e);
}
- }
+ };
@computed get theGraph() {
- return <div id={`${this._plotId}`} ref={r => r && this.createGraph(r)} style={{ position: "absolute", width: "100%", height: "100%" }}
- onPointerDown={e => e.stopPropagation()} />;
+ return <div id={`${this._plotId}`} ref={r => r && this.createGraph(r)} style={{ position: 'absolute', width: '100%', height: '100%' }} onPointerDown={e => e.stopPropagation()} />;
}
render() {
TraceMobx();
- return (<div
- style={{
- pointerEvents: !this.isContentActive() ? "all" : undefined,
- width: this.props.PanelWidth(),
- height: this.props.PanelHeight()
- }}
- >
- {this.theGraph}
- <div style={{
- display: this.props.isSelected() ? "none" : undefined, position: "absolute", width: "100%", height: "100%",
- pointerEvents: "all"
- }} />
- </div>);
+ return (
+ <div
+ style={{
+ pointerEvents: !this.isContentActive() ? 'all' : undefined,
+ width: this.props.PanelWidth(),
+ height: this.props.PanelHeight(),
+ }}>
+ {this.theGraph}
+ <div
+ style={{
+ display: this.props.isSelected() ? 'none' : undefined,
+ position: 'absolute',
+ width: '100%',
+ height: '100%',
+ pointerEvents: 'all',
+ }}
+ />
+ </div>
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index ffa839fcb..9590bcb15 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -148,8 +148,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
cropping.title = 'crop: ' + this.rootDoc.title;
cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
cropping.y = NumCast(this.rootDoc.y);
- cropping._width = anchw * (this.props.scaling?.() || 1);
- cropping._height = anchh * (this.props.scaling?.() || 1);
+ cropping._width = anchw * (this.props.NativeDimScaling?.() || 1);
+ cropping._height = anchh * (this.props.NativeDimScaling?.() || 1);
cropping.isLinkButton = undefined;
const croppingProto = Doc.GetProto(cropping);
croppingProto.annotationOn = undefined;
@@ -384,7 +384,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
render() {
TraceMobx();
const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
- const borderRadius = borderRad?.includes('px') ? `${Number(borderRad.split('px')[0]) / (this.props.scaling?.() || 1)}px` : borderRad;
+ const borderRadius = borderRad?.includes('px') ? `${Number(borderRad.split('px')[0]) / (this.props.NativeDimScaling?.() || 1)}px` : borderRad;
return (
<div
className="imageBox"
@@ -407,7 +407,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
PanelHeight={this.props.PanelHeight}
ScreenToLocalTransform={this.screenToLocalTransform}
select={emptyFunction}
- scaling={returnOne}
+ NativeDimScaling={returnOne}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
removeDocument={this.removeDocument}
moveDocument={this.moveDocument}
@@ -420,7 +420,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
rootDoc={this.rootDoc}
scrollTop={0}
down={this._marqueeing}
- scaling={this.props.scaling}
+ scaling={this.props.NativeDimScaling}
docView={this.props.docViewPath().slice(-1)[0]}
addDocument={this.addDocument}
finishMarquee={this.finishMarquee}
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 85a8622ec..5102eae51 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -136,7 +136,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
return (
<div
className={`linkAnchorBox-cont${small ? '-small' : ''}`}
- onPointerLeave={LinkDocPreview.Clear}
+ //onPointerLeave={} //LinkDocPreview.Clear}
onPointerEnter={e =>
LinkDocPreview.SetLinkInfo({
docProps: this.props,
diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx
index ccac66996..91bd505c5 100644
--- a/src/client/views/nodes/LinkDescriptionPopup.tsx
+++ b/src/client/views/nodes/LinkDescriptionPopup.tsx
@@ -1,26 +1,24 @@
-import React = require("react");
-import { action, observable } from "mobx";
-import { observer } from "mobx-react";
-import { Doc } from "../../../fields/Doc";
-import { LinkManager } from "../../util/LinkManager";
-import "./LinkDescriptionPopup.scss";
-import { TaskCompletionBox } from "./TaskCompletedBox";
-
+import React = require('react');
+import { action, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc } from '../../../fields/Doc';
+import { LinkManager } from '../../util/LinkManager';
+import './LinkDescriptionPopup.scss';
+import { TaskCompletionBox } from './TaskCompletedBox';
@observer
export class LinkDescriptionPopup extends React.Component<{}> {
-
@observable public static descriptionPopup: boolean = false;
- @observable public static showDescriptions: string = "ON";
+ @observable public static showDescriptions: string = 'ON';
@observable public static popupX: number = 700;
@observable public static popupY: number = 350;
- @observable description: string = "";
+ @observable description: string = '';
@observable popupRef = React.createRef<HTMLDivElement>();
@action
descriptionChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
this.description = e.currentTarget.value;
- }
+ };
@action
onDismiss = (add: boolean) => {
@@ -28,7 +26,7 @@ export class LinkDescriptionPopup extends React.Component<{}> {
if (add) {
LinkManager.currentLink && (Doc.GetProto(LinkManager.currentLink).description = this.description);
}
- }
+ };
@action
onClick = (e: PointerEvent) => {
@@ -36,35 +34,43 @@ export class LinkDescriptionPopup extends React.Component<{}> {
LinkDescriptionPopup.descriptionPopup = false;
TaskCompletionBox.taskCompleted = false;
}
- }
+ };
@action
componentDidMount() {
- document.addEventListener("pointerdown", this.onClick);
+ document.addEventListener('pointerdown', this.onClick, true);
}
componentWillUnmount() {
- document.removeEventListener("pointerdown", this.onClick);
+ document.removeEventListener('pointerdown', this.onClick, true);
}
render() {
- return <div className="linkDescriptionPopup" ref={this.popupRef}
- style={{
- left: LinkDescriptionPopup.popupX ? LinkDescriptionPopup.popupX : 700,
- top: LinkDescriptionPopup.popupY ? LinkDescriptionPopup.popupY : 350,
- }}>
- <input className="linkDescriptionPopup-input"
- onKeyDown={e => e.stopPropagation()}
- onKeyPress={e => e.key === "Enter" && this.onDismiss(true)}
- placeholder={"(Optional) Enter link description..."}
- onChange={(e) => this.descriptionChanged(e)}>
- </input>
- <div className="linkDescriptionPopup-btn">
- <div className="linkDescriptionPopup-btn-dismiss"
- onPointerDown={e => this.onDismiss(false)}> Dismiss </div>
- <div className="linkDescriptionPopup-btn-add"
- onPointerDown={e => this.onDismiss(true)}> Add </div>
+ return (
+ <div
+ className="linkDescriptionPopup"
+ ref={this.popupRef}
+ style={{
+ left: LinkDescriptionPopup.popupX ? LinkDescriptionPopup.popupX : 700,
+ top: LinkDescriptionPopup.popupY ? LinkDescriptionPopup.popupY : 350,
+ }}>
+ <input
+ className="linkDescriptionPopup-input"
+ onKeyDown={e => e.stopPropagation()}
+ onKeyPress={e => e.key === 'Enter' && this.onDismiss(true)}
+ placeholder={'(Optional) Enter link description...'}
+ onChange={e => this.descriptionChanged(e)}></input>
+ <div className="linkDescriptionPopup-btn">
+ <div className="linkDescriptionPopup-btn-dismiss" onPointerDown={e => this.onDismiss(false)}>
+ {' '}
+ Dismiss{' '}
+ </div>
+ <div className="linkDescriptionPopup-btn-add" onPointerDown={e => this.onDismiss(true)}>
+ {' '}
+ Add{' '}
+ </div>
+ </div>
</div>
- </div>;
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/LinkDocPreview.scss b/src/client/views/nodes/LinkDocPreview.scss
index 3febbcecb..c68e55f73 100644
--- a/src/client/views/nodes/LinkDocPreview.scss
+++ b/src/client/views/nodes/LinkDocPreview.scss
@@ -8,6 +8,7 @@
border-bottom: 8px solid white;
border-right: 8px solid white;
z-index: 2004;
+ cursor: pointer;
.linkDocPreview-inner {
background-color: white;
@@ -63,13 +64,16 @@
cursor: pointer;
}
}
+ }
- .linkDocPreview-preview-wrapper {
- overflow: hidden;
- align-content: center;
- justify-content: center;
- background-color: rgb(160, 160, 160);
- }
+ .linkDocPreview-preview-wrapper {
+ overflow: hidden;
+ align-content: center;
+ justify-content: center;
+ pointer-events: all;
+ background-color: rgb(160, 160, 160);
+ overflow: auto;
+ cursor: grab;
}
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index c2fb5d4ec..93ca22d5d 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -3,20 +3,21 @@ import { Tooltip } from '@material-ui/core';
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import wiki from 'wikijs';
-import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocCastAsync } from '../../../fields/Doc';
-import { NumCast, StrCast, Cast } from '../../../fields/Types';
-import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, setupMoveUpEvents, Utils } from '../../../Utils';
+import { Doc, DocCastAsync, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils';
import { DocServer } from '../../DocServer';
import { Docs, DocUtils } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { DragManager } from '../../util/DragManager';
+import { LinkFollower } from '../../util/LinkFollower';
import { LinkManager } from '../../util/LinkManager';
import { Transform } from '../../util/Transform';
import { undoBatch } from '../../util/UndoManager';
import { DocumentView, DocumentViewSharedProps } from './DocumentView';
import './LinkDocPreview.scss';
import React = require('react');
-import { DocumentType } from '../../documents/DocumentTypes';
-import { DragManager } from '../../util/DragManager';
-import { LinkFollower } from '../../util/LinkFollower';
+import { LinkEditor } from '../linking/LinkEditor';
interface LinkDocPreviewProps {
linkDoc?: Doc;
@@ -36,6 +37,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
}
_infoRef = React.createRef<HTMLDivElement>();
+ _linkDocRef = React.createRef<HTMLDivElement>();
@observable public static LinkInfo: Opt<LinkDocPreviewProps>;
@observable _targetDoc: Opt<Doc>;
@observable _linkDoc: Opt<Doc>;
@@ -65,16 +67,16 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
}
componentDidMount() {
this.init();
- document.addEventListener('pointerdown', this.onPointerDown);
+ document.addEventListener('pointerdown', this.onPointerDown, true);
}
componentWillUnmount() {
LinkDocPreview.SetLinkInfo(undefined);
- document.removeEventListener('pointerdown', this.onPointerDown);
+ document.removeEventListener('pointerdown', this.onPointerDown, true);
}
onPointerDown = (e: PointerEvent) => {
- !this._infoRef.current?.contains(e.target as any) && LinkDocPreview.Clear(); // close preview when not clicking anywhere other than the info bar of the preview
+ !this._linkDocRef.current?.contains(e.target as any) && LinkDocPreview.Clear(); // close preview when not clicking anywhere other than the info bar of the preview
};
@computed get href() {
@@ -117,13 +119,15 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
}
return undefined;
}
- deleteLink = (e: React.PointerEvent) => {
+ @observable _showEditor = false;
+ editLink = (e: React.PointerEvent): void => {
+ LinkManager.currentLink = this.props.linkDoc;
setupMoveUpEvents(
this,
e,
returnFalse,
emptyFunction,
- undoBatch(() => this._linkDoc && LinkManager.Instance.deleteLink(this._linkDoc))
+ action(() => (this._showEditor = !this._showEditor))
);
};
nextHref = (e: React.PointerEvent) => {
@@ -143,7 +147,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
);
};
- followLink = (e: React.PointerEvent) => {
+ followLink = () => {
if (this._linkDoc && this._linkSrc) {
LinkDocPreview.Clear();
LinkFollower.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false);
@@ -151,6 +155,9 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }), 'add:right');
}
};
+
+ followLinkPointerDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, returnFalse, emptyFunction, this.followLink);
+
width = () => {
if (!this._targetDoc) return 225;
if (this._targetDoc[WidthSym]() < this._targetDoc?.[HeightSym]()) {
@@ -167,15 +174,8 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
};
@computed get previewHeader() {
return !this._linkDoc || !this._targetDoc || !this._linkSrc ? null : (
- <div className="linkDocPreview-info" ref={this._infoRef}>
- <div
- className="linkDocPreview-title"
- style={{ pointerEvents: 'all' }}
- onPointerDown={e => {
- DragManager.StartDocumentDrag([this._infoRef.current!], new DragManager.DocumentDragData([this._targetDoc!]), e.pageX, e.pageY);
- e.stopPropagation();
- LinkDocPreview.Clear();
- }}>
+ <div className="linkDocPreview-info">
+ <div className="linkDocPreview-title" style={{ pointerEvents: 'all' }}>
{StrCast(this._targetDoc.title).length > 16 ? StrCast(this._targetDoc.title).substr(0, 16) + '...' : StrCast(this._targetDoc.title)}
<p className="linkDocPreview-description"> {StrCast(this._linkDoc.description)}</p>
</div>
@@ -186,9 +186,9 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
</div>
</Tooltip>
- <Tooltip title={<div className="dash-tooltip">Delete Link</div>} placement="top">
- <div className="linkDocPreview-button" onPointerDown={this.deleteLink}>
- <FontAwesomeIcon className="linkDocPreview-fa-icon" icon="trash" color="white" size="sm" />
+ <Tooltip title={<div className="dash-tooltip">Edit Link</div>} placement="top">
+ <div className="linkDocPreview-button" onPointerDown={this.editLink}>
+ <FontAwesomeIcon className="linkDocPreview-fa-icon" icon="edit" color="white" size="sm" />
</div>
</Tooltip>
</div>
@@ -201,7 +201,27 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
return (!this._linkDoc || !this._targetDoc || !this._linkSrc) && !this._toolTipText ? null : (
<div className="linkDocPreview-inner">
{!this.props.showHeader ? null : this.previewHeader}
- <div className="linkDocPreview-preview-wrapper" style={{ maxHeight: this._toolTipText ? '100%' : undefined, overflow: 'auto' }}>
+ <div
+ className="linkDocPreview-preview-wrapper"
+ onPointerDown={e =>
+ setupMoveUpEvents(
+ this,
+ e,
+ (e, down, delta) => {
+ if (Math.abs(e.clientX - down[0]) + Math.abs(e.clientY - down[1]) > 100) {
+ DragManager.StartDocumentDrag([this._infoRef.current!], new DragManager.DocumentDragData([this._targetDoc!]), e.pageX, e.pageY);
+ LinkDocPreview.Clear();
+ return true;
+ }
+ return false;
+ },
+ emptyFunction,
+ this.followLink,
+ true
+ )
+ }
+ ref={this._infoRef}
+ style={{ maxHeight: this._toolTipText ? '100%' : undefined }}>
{this._toolTipText ? (
this._toolTipText
) : (
@@ -217,8 +237,9 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
docViewPath={returnEmptyDoclist}
ScreenToLocalTransform={Transform.Identity}
isDocumentActive={returnFalse}
- isContentActive={emptyFunction}
+ isContentActive={returnFalse}
addDocument={returnFalse}
+ showTitle={returnEmptyString}
removeDocument={returnFalse}
addDocTab={returnFalse}
pinToPres={returnFalse}
@@ -232,6 +253,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
suppressSetHeight={true}
PanelWidth={this.width}
PanelHeight={this.height}
+ pointerEvents={returnNone}
focus={DocUtils.DefaultFocus}
whenChildContentsActiveChanged={returnFalse}
ignoreAutoHeight={true} // need to ignore autoHeight otherwise autoHeight text boxes will expand beyond the preview panel size.
@@ -248,8 +270,13 @@ 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() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
- {this.docPreview}
+ <div
+ className="linkDocPreview"
+ ref={this._linkDocRef}
+ onPointerDown={this.followLinkPointerDown}
+ style={{ left: this.props.location[0], top: this.props.location[1], width: this._showEditor ? 'auto' : this.width() + borders, height: this._showEditor ? 'max-content' : this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
+ {this._showEditor ? null : this.docPreview}
+ {!this._showEditor || !this._linkSrc || !this._linkDoc ? null : <LinkEditor sourceDoc={this._linkSrc} linkDoc={this._linkDoc} showLinks={action(() => (this._showEditor = !this._showEditor))} />}
</div>
);
}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index bf4c029b2..6479e933e 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,17 +1,17 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
-import { action, computed, IReactionDisposer, observable, ObservableMap } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
import { InkTool } from '../../../../fields/InkField';
import { NumCast, StrCast } from '../../../../fields/Types';
-import { TraceMobx } from '../../../../fields/util';
import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
import { Docs } from '../../../documents/Documents';
import { DragManager } from '../../../util/DragManager';
import { SnappingManager } from '../../../util/SnappingManager';
+import { UndoManager } from '../../../util/UndoManager';
import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
import { Colors } from '../../global/globalEnums';
@@ -19,7 +19,6 @@ import { MarqueeAnnotator } from '../../MarqueeAnnotator';
import { AnchorMenu } from '../../pdf/AnchorMenu';
import { Annotation } from '../../pdf/Annotation';
import { SidebarAnnos } from '../../SidebarAnnos';
-import { StyleProp } from '../../StyleProvider';
import { FieldView, FieldViewProps } from '../FieldView';
import './MapBox.scss';
import { MapBoxInfoWindow } from './MapBoxInfoWindow';
@@ -368,23 +367,27 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
setupMoveUpEvents(
this,
e,
- (e, down, delta) => {
- const localDelta = this.props
- .ScreenToLocalTransform()
- .scale(this.props.scaling?.() || 1)
- .transformDirection(delta[0], delta[1]);
- const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
- const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
- const ratio = (curNativeWidth + localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
- if (ratio >= 1) {
- this.layoutDoc.nativeWidth = nativeWidth * ratio;
- this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0];
- this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
- }
- return false;
- },
+ (e, down, delta) =>
+ runInAction(() => {
+ const localDelta = this.props
+ .ScreenToLocalTransform()
+ .scale(this.props.NativeDimScaling?.() || 1)
+ .transformDirection(delta[0], delta[1]);
+ const fullWidth = this.layoutDoc[WidthSym]();
+ const mapWidth = fullWidth - this.sidebarWidth();
+ if (this.sidebarWidth() + localDelta[0] > 0) {
+ this._showSidebar = true;
+ this.layoutDoc._width = fullWidth + localDelta[0];
+ this.layoutDoc._sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
+ } else {
+ this._showSidebar = false;
+ this.layoutDoc._width = mapWidth;
+ this.layoutDoc._sidebarWidthPercent = '0%';
+ }
+ return false;
+ }),
emptyFunction,
- this.toggleSidebar
+ () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map')
);
};
@@ -448,21 +451,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
* Handles toggle of sidebar on click the little comment button
*/
@computed get sidebarHandle() {
- TraceMobx();
- const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length;
- const color = !annotated ? Colors.WHITE : Colors.BLACK;
- const backgroundColor = !annotated ? (this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK) : this.props.styleProvider?.(this.rootDoc, this.props as any, StyleProp.WidgetColor + (annotated ? ':annotated' : ''));
- return !annotated ? null : (
+ return (
<div
- className="formattedTextBox-sidebar-handle"
- onPointerDown={this.sidebarDown}
+ className="mapBox-overlayButton-sidebar"
+ key="sidebar"
+ title="Toggle Sidebar"
style={{
- left: `max(0px, calc(100% - ${this.sidebarWidthPercent} - 17px))`,
- backgroundColor: backgroundColor,
- color: color,
- opacity: annotated ? 1 : undefined,
- }}>
- <FontAwesomeIcon icon={'comment-alt'} />
+ display: !this.props.isContentActive() ? 'none' : undefined,
+ top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
+ backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
+ }}
+ onPointerDown={this.sidebarBtnDown}>
+ <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
</div>
);
}
@@ -470,13 +470,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
// TODO: Adding highlight box layer to Maps
@action
toggleSidebar = () => {
+ //1.2 * w * ? = .2 * w .2/1.2
const prevWidth = this.sidebarWidth();
- this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%';
- this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
+ this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
+ this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
};
sidebarDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), false);
+ setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true);
};
sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
const bounds = this._ref.current!.getBoundingClientRect();
@@ -553,8 +554,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
// }
};
- panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
- panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
+ panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
+ panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
@@ -583,13 +584,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
// docFilters={docFilters || this.props.docFilters}
// dontRenderDocuments={docFilters ? false : true}
// select={emptyFunction}
- // ContentScaling={returnOne}
// bringToFront={emptyFunction}
// whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
// removeDocument={this.removeDocument}
// moveDocument={this.moveDocument}
// addDocument={this.sidebarAddDocument}
- // childPointerEvents={true}
+ // childPointerEvents={"all"}
// pointerEvents={Doc.ActiveTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
return (
<div className="mapBox" ref={this._ref}>
@@ -605,7 +605,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
{this.annotationLayer}
<GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
<Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
- <input className="mapBox-input" ref={this.inputRef} type="text" placeholder="Enter location" />
+ <input className="mapBox-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
</Autocomplete>
{this.renderMarkers()}
@@ -643,7 +643,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
)}
</div>
{/* </LoadScript > */}
- <div className="mapBox-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+ <div className="mapBox-sidebar" style={{ position: 'absolute', right: 0, height: '100%', width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
<SidebarAnnos
ref={this._sidebarRef}
{...this.props}
@@ -660,18 +660,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
removeDocument={this.sidebarRemoveDocument}
/>
</div>
- <div
- className="mapBox-overlayButton-sidebar"
- key="sidebar"
- title="Toggle Sidebar"
- style={{
- display: !this.props.isContentActive() ? 'none' : undefined,
- top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
- backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
- }}
- onPointerDown={this.sidebarBtnDown}>
- <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
- </div>
+ {this.sidebarHandle}
</div>
);
}
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index 72569135b..00bedafbe 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -1,5 +1,5 @@
import { InfoWindow } from '@react-google-maps/api';
-import { action, computed } from 'mobx';
+import { action } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc } from '../../../../fields/Doc';
@@ -7,6 +7,7 @@ import { Id } from '../../../../fields/FieldSymbols';
import { emptyFunction, OmitKeys, returnAll, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents } from '../../../../Utils';
import { Docs } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
+import { CollectionNoteTakingView } from '../../collections/CollectionNoteTakingView';
import { CollectionStackingView } from '../../collections/CollectionStackingView';
import { ViewBoxAnnotatableProps } from '../../DocComponent';
import { FieldViewProps } from '../FieldView';
@@ -40,7 +41,7 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
});
};
- _stack: CollectionStackingView | null | undefined;
+ _stack: CollectionStackingView | CollectionNoteTakingView | null | undefined;
childFitWidth = (doc: Doc) => doc.type === DocumentType.RTF;
addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, 'data', d), true as boolean);
removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, 'data', d), true as boolean);
@@ -62,7 +63,7 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
setHeight={emptyFunction}
isAnnotationOverlay={false}
select={emptyFunction}
- scaling={returnOne}
+ NativeDimScaling={returnOne}
isContentActive={returnTrue}
chromeHidden={true}
rootSelected={returnFalse}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 5b9d90780..345407c2f 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -8,17 +8,17 @@ import { Id } from '../../../fields/FieldSymbols';
import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField, PdfField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
+import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
import { KeyCodes } from '../../util/KeyCodes';
import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
import { Colors } from '../global/globalEnums';
import { CreateImage } from '../nodes/WebBoxRenderer';
-import { AnchorMenu } from '../pdf/AnchorMenu';
import { PDFViewer } from '../pdf/PDFViewer';
import { SidebarAnnos } from '../SidebarAnnos';
import { FieldView, FieldViewProps } from './FieldView';
@@ -26,7 +26,6 @@ import { ImageBox } from './ImageBox';
import './PDFBox.scss';
import { VideoBox } from './VideoBox';
import React = require('react');
-import { CollectionFreeFormView } from '../collections/collectionFreeForm';
@observer
export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
@@ -105,8 +104,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const anchx = NumCast(cropping.x);
const anchy = NumCast(cropping.y);
- const anchw = cropping[WidthSym]() * (this.props.scaling?.() || 1);
- const anchh = cropping[HeightSym]() * (this.props.scaling?.() || 1);
+ const anchw = cropping[WidthSym]() * (this.props.NativeDimScaling?.() || 1);
+ const anchh = cropping[HeightSym]() * (this.props.NativeDimScaling?.() || 1);
const viewScale = 1;
cropping.title = 'crop: ' + this.rootDoc.title;
cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
@@ -291,11 +290,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
(e, down, delta) => {
const localDelta = this.props
.ScreenToLocalTransform()
- .scale(this.props.scaling?.() || 1)
+ .scale(this.props.NativeDimScaling?.() || 1)
.transformDirection(delta[0], delta[1]);
const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
- const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.scaling?.() || 1)) / nativeWidth;
+ const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.NativeDimScaling?.() || 1)) / nativeWidth;
if (ratio >= 1) {
this.layoutDoc.nativeWidth = nativeWidth * ratio;
onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
@@ -303,8 +302,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
return false;
},
- () => batch.end(),
- () => this.toggleSidebar()
+ (e, movement, isClick) => !isClick && batch.end(),
+ () => {
+ this.toggleSidebar();
+ batch.end();
+ }
);
};
@observable _previewNativeWidth: Opt<number> = undefined;
@@ -388,18 +390,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
/>
{this._pageControls ? pageBtns : null}
</div>
- <div
- className="pdfBox-sidebarBtn"
- key="sidebar"
- title="Toggle Sidebar"
- style={{
- display: !this.props.isContentActive() ? 'none' : undefined,
- top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
- backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
- }}
- onPointerDown={e => this.sidebarBtnDown(e, true)}>
- <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
- </div>
+ {this.sidebarHandle}
</div>
);
}
@@ -430,13 +421,28 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@computed get SidebarShown() {
return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
}
+ @computed get sidebarHandle() {
+ return (
+ <div
+ className="pdfBox-sidebarBtn"
+ key="sidebar"
+ title="Toggle Sidebar"
+ style={{
+ display: !this.props.isContentActive() ? 'none' : undefined,
+ top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
+ backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
+ }}
+ onPointerDown={e => this.sidebarBtnDown(e, true)}>
+ <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
+ </div>
+ );
+ }
- contentScaling = () => 1;
isPdfContentActive = () => this.isAnyChildContentActive() || this.props.isSelected();
@computed get renderPdfView() {
TraceMobx();
const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
- const scale = previewScale * (this.props.scaling?.() || 1);
+ const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
return (
<div
className={'pdfBox'}
@@ -471,23 +477,24 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
removeDocument={this.removeDocument}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
crop={this.crop}
- ContentScaling={returnOne}
/>
</div>
- <SidebarAnnos
- ref={this._sidebarRef}
- {...this.props}
- rootDoc={this.rootDoc}
- layoutDoc={this.layoutDoc}
- dataDoc={this.dataDoc}
- setHeight={emptyFunction}
- nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth)}
- showSidebar={this.SidebarShown}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- sidebarAddDocument={this.sidebarAddDocument}
- moveDocument={this.moveDocument}
- removeDocument={this.removeDocument}
- />
+ <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.layoutDoc[WidthSym]()}%` }}>
+ <SidebarAnnos
+ ref={this._sidebarRef}
+ {...this.props}
+ rootDoc={this.rootDoc}
+ layoutDoc={this.layoutDoc}
+ dataDoc={this.dataDoc}
+ setHeight={emptyFunction}
+ nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth)}
+ showSidebar={this.SidebarShown}
+ whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+ sidebarAddDocument={this.sidebarAddDocument}
+ moveDocument={this.moveDocument}
+ removeDocument={this.removeDocument}
+ />
+ </div>
{this.settingsPanel()}
</div>
);
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 942072524..76a24d831 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -295,7 +295,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
isAnnotationOverlay={true}
select={emptyFunction}
isContentActive={returnFalse}
- scaling={returnOne}
+ NativeDimScaling={returnOne}
whenChildContentsActiveChanged={emptyFunction}
removeDocument={returnFalse}
moveDocument={returnFalse}
@@ -317,7 +317,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
isAnnotationOverlay={true}
select={emptyFunction}
isContentActive={emptyFunction}
- scaling={returnOne}
+ NativeDimScaling={returnOne}
xPadding={25}
yPadding={10}
whenChildContentsActiveChanged={emptyFunction}
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 05ff40f22..1c9b0bc0e 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -6,7 +6,7 @@ import { Doc } from '../../../fields/Doc';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
import { returnEmptyString } from '../../../Utils';
import { DragManager } from '../../util/DragManager';
@@ -60,6 +60,14 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
constructor(props: any) {
super(props);
+ if (!this.compileParams.length) {
+ const params = ScriptCast(this.rootDoc[this.props.fieldKey])?.script.options.params as { [key: string]: any };
+ if (params) {
+ this.compileParams = Array.from(Object.keys(params))
+ .filter(p => !p.startsWith('_'))
+ .map(key => key + ':' + params[key]);
+ }
+ }
}
// vars included in fields that store parameters types and names and the script itself
@@ -70,30 +78,30 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
return this.compileParams.map(p => p.split(':')[1].trim());
}
@computed({ keepAlive: true }) get rawScript() {
- return StrCast(this.dataDoc[this.props.fieldKey + '-rawScript'], '');
+ return ScriptCast(this.rootDoc[this.fieldKey])?.script.originalScript ?? '';
}
@computed({ keepAlive: true }) get functionName() {
- return StrCast(this.dataDoc[this.props.fieldKey + '-functionName'], '');
+ return StrCast(this.rootDoc[this.props.fieldKey + '-functionName'], '');
}
@computed({ keepAlive: true }) get functionDescription() {
- return StrCast(this.dataDoc[this.props.fieldKey + '-functionDescription'], '');
+ return StrCast(this.rootDoc[this.props.fieldKey + '-functionDescription'], '');
}
@computed({ keepAlive: true }) get compileParams() {
- return Cast(this.dataDoc[this.props.fieldKey + '-params'], listSpec('string'), []);
+ return Cast(this.rootDoc[this.props.fieldKey + '-params'], listSpec('string'), []);
}
set rawScript(value) {
- this.dataDoc[this.props.fieldKey + '-rawScript'] = value;
+ Doc.SetInPlace(this.rootDoc, this.props.fieldKey, new ScriptField(undefined, undefined, value), true);
}
set functionName(value) {
- this.dataDoc[this.props.fieldKey + '-functionName'] = value;
+ Doc.SetInPlace(this.rootDoc, this.props.fieldKey + '-functionName', value, true);
}
set functionDescription(value) {
- this.dataDoc[this.props.fieldKey + '-functionDescription'] = value;
+ Doc.SetInPlace(this.rootDoc, this.props.fieldKey + '-functionDescription', value, true);
}
set compileParams(value) {
- this.dataDoc[this.props.fieldKey + '-params'] = new List<string>(value);
+ Doc.SetInPlace(this.rootDoc, this.props.fieldKey + '-params', new List<string>(value), true);
}
getValue(result: any, descrip: boolean) {
@@ -107,8 +115,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
@action
componentDidMount() {
- this.rawScript = ScriptCast(this.dataDoc[this.props.fieldKey])?.script?.originalScript ?? this.rawScript;
-
+ this.rawText = this.rawScript;
const observer = new _global.ResizeObserver(
action((entries: any) => {
const area = document.querySelector('textarea');
@@ -171,13 +178,13 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
const params: ScriptParam = {};
this.compileParams.forEach(p => (params[p.split(':')[0].trim()] = p.split(':')[1].trim()));
- const result = CompileScript(this.rawScript, {
+ const result = CompileScript(this.rawText, {
editable: true,
transformer: DocumentIconContainer.getTransformer(),
params,
typecheck: false,
});
- this.dataDoc[this.fieldKey] = result.compiled ? new ScriptField(result) : undefined;
+ Doc.SetInPlace(this.rootDoc, this.fieldKey, result.compiled ? new ScriptField(result, undefined, this.rawText) : new ScriptField(undefined, undefined, this.rawText), true);
this.onError(result.compiled ? undefined : result.errors);
return result.compiled;
};
@@ -187,9 +194,9 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
onRun = () => {
if (this.onCompile()) {
const bindings: { [name: string]: any } = {};
- this.paramsNames.forEach(key => (bindings[key] = this.dataDoc[key]));
+ this.paramsNames.forEach(key => (bindings[key] = this.rootDoc[key]));
// binds vars so user doesnt have to refer to everything as self.<var>
- ScriptCast(this.dataDoc[this.fieldKey], null)?.script.run({ self: this.rootDoc, this: this.layoutDoc, ...bindings }, this.onError);
+ ScriptCast(this.rootDoc[this.fieldKey], null)?.script.run({ self: this.rootDoc, this: this.layoutDoc, ...bindings }, this.onError);
}
};
@@ -257,14 +264,14 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
// sets field of the corresponding field key (param name) to be dropped document
@action
onDrop = (e: Event, de: DragManager.DropEvent, fieldKey: string) => {
- this.dataDoc[fieldKey] = de.complete.docDragData?.droppedDocuments[0];
+ Doc.SetInPlace(this.rootDoc, fieldKey, de.complete.docDragData?.droppedDocuments[0], true);
e.stopPropagation();
};
// deletes a param from all areas in which it is stored
@action
onDelete = (num: number) => {
- this.dataDoc[this.paramsNames[num]] = undefined;
+ Doc.SetInPlace(this.rootDoc, this.paramsNames[num], undefined, true);
this.compileParams.splice(num, 1);
return true;
};
@@ -274,7 +281,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
viewChanged = (e: React.ChangeEvent, name: string) => {
//@ts-ignore
const val = e.target.selectedOptions[0].value;
- this.dataDoc[name] = val[0] === 'S' ? val.substring(1) : val[0] === 'N' ? parseInt(val.substring(1)) : val.substring(1) === 'true';
+ Doc.SetInPlace(this.rootDoc, name, val[0] === 'S' ? val.substring(1) : val[0] === 'N' ? parseInt(val.substring(1)) : val.substring(1) === 'true', true);
};
// creates a copy of the script document
@@ -330,8 +337,8 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
maxHeight={72}
height={35}
fontSize={14}
- contents={this.dataDoc[parameter]?.title ?? 'undefined'}
- GetValue={() => this.dataDoc[parameter]?.title ?? 'undefined'}
+ contents={StrCast(DocCast(this.rootDoc[parameter])?.title, 'undefined')}
+ GetValue={() => StrCast(DocCast(this.rootDoc[parameter])?.title, 'undefined')}
SetValue={action((value: string) => {
const script = CompileScript(value, {
addReturn: true,
@@ -341,7 +348,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
const results = script.compiled && script.run();
if (results && results.success) {
this._errorMessage = '';
- this.dataDoc[parameter] = results.result;
+ Doc.SetInPlace(this.rootDoc, parameter, results.result, true);
return true;
}
this._errorMessage = 'invalid document';
@@ -354,7 +361,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
// rendering when a string's value can be set in applied UI
renderBasicType(parameter: string, isNum: boolean) {
- const strVal = isNum ? NumCast(this.dataDoc[parameter]).toString() : StrCast(this.dataDoc[parameter]);
+ const strVal = isNum ? NumCast(this.rootDoc[parameter]).toString() : StrCast(this.rootDoc[parameter]);
return (
<div className="scriptingBox-paramInputs" style={{ overflowY: 'hidden' }}>
<EditableView
@@ -368,7 +375,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
const setValue = isNum ? parseInt(value) : value;
if (setValue !== undefined && setValue !== ' ') {
this._errorMessage = '';
- this.dataDoc[parameter] = setValue;
+ Doc.SetInPlace(this.rootDoc, parameter, setValue, true);
return true;
}
this._errorMessage = 'invalid input';
@@ -389,7 +396,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
className="scriptingBox-viewPicker"
onPointerDown={e => e.stopPropagation()}
onChange={e => this.viewChanged(e, parameter)}
- value={typeof this.dataDoc[parameter] === 'string' ? 'S' + StrCast(this.dataDoc[parameter]) : typeof this.dataDoc[parameter] === 'number' ? 'N' + NumCast(this.dataDoc[parameter]) : 'B' + BoolCast(this.dataDoc[parameter])}>
+ value={typeof this.rootDoc[parameter] === 'string' ? 'S' + StrCast(this.rootDoc[parameter]) : typeof this.rootDoc[parameter] === 'number' ? 'N' + NumCast(this.rootDoc[parameter]) : 'B' + BoolCast(this.rootDoc[parameter])}>
{types.map(type => (
<option className="scriptingBox-viewOption" value={(typeof type === 'string' ? 'S' : typeof type === 'number' ? 'N' : 'B') + type}>
{' '}
@@ -442,7 +449,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
@action
handleFunc(pos: number) {
- const scriptString = this.rawScript.slice(0, pos - 2);
+ const scriptString = this.rawText.slice(0, pos - 2);
this._currWord = scriptString.split(' ')[scriptString.split(' ').length - 1];
this._suggestions = [StrCast(this._scriptingParams[this._currWord])];
return this._suggestions;
@@ -474,7 +481,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
}
getSuggestedParams(pos: number) {
- const firstScript = this.rawScript.slice(0, pos);
+ const firstScript = this.rawText.slice(0, pos);
const indexP = firstScript.lastIndexOf('.');
const indexS = firstScript.lastIndexOf(' ');
const func = firstScript.slice((indexP > indexS ? indexP : indexS) + 1, firstScript.length + 1);
@@ -494,8 +501,9 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
@action
keyHandler(e: any, pos: number) {
+ e.stopPropagation();
if (this._lastChar === 'Enter') {
- this.rawScript = this.rawScript + ' ';
+ this.rawText = this.rawText + ' ';
}
if (e.key === '(') {
this.suggestionPos();
@@ -504,7 +512,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
this._scriptSuggestedParams = this.getSuggestedParams(pos);
if (this._scriptParamsText !== undefined && this._scriptParamsText.length > 0) {
- if (this.rawScript[pos - 2] !== '(') {
+ if (this.rawText[pos - 2] !== '(') {
this._paramSuggestion = true;
}
}
@@ -515,22 +523,22 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
if (this._lastChar === '(') {
this._paramSuggestion = false;
} else if (this._lastChar === ')') {
- if (this.rawScript.slice(0, this.rawScript.length - 1).split('(').length - 1 > this.rawScript.slice(0, this.rawScript.length - 1).split(')').length - 1) {
+ if (this.rawText.slice(0, this.rawText.length - 1).split('(').length - 1 > this.rawText.slice(0, this.rawText.length - 1).split(')').length - 1) {
if (this._scriptParamsText.length > 0) {
this._paramSuggestion = true;
}
}
}
- } else if (this.rawScript.split('(').length - 1 <= this.rawScript.split(')').length - 1) {
+ } else if (this.rawText.split('(').length - 1 <= this.rawText.split(')').length - 1) {
this._paramSuggestion = false;
}
}
- this._lastChar = e.key === 'Backspace' ? this.rawScript[this.rawScript.length - 2] : e.key;
+ this._lastChar = e.key === 'Backspace' ? this.rawText[this.rawText.length - 2] : e.key;
if (this._paramSuggestion) {
const parameters = this._scriptParamsText.split(',');
- const index = this.rawScript.lastIndexOf('(');
- const enteredParams = this.rawScript.slice(index, this.rawScript.length);
+ const index = this.rawText.lastIndexOf('(');
+ const enteredParams = this.rawText.slice(index, this.rawText.length);
const splitEntered = enteredParams.split(',');
const numEntered = splitEntered.length;
@@ -564,15 +572,16 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
handlePosChange(number: any) {
this._caretPos = number;
if (this._caretPos === 0) {
- this.rawScript = ' ' + this.rawScript;
+ this.rawText = ' ' + this.rawText;
} else if (this._spaced) {
this._spaced = false;
- if (this.rawScript[this._caretPos - 1] === ' ') {
- this.rawScript = this.rawScript.slice(0, this._caretPos - 1) + this.rawScript.slice(this._caretPos, this.rawScript.length);
+ if (this.rawText[this._caretPos - 1] === ' ') {
+ this.rawText = this.rawText.slice(0, this._caretPos - 1) + this.rawText.slice(this._caretPos, this.rawText.length);
}
}
}
+ @observable rawText: string = '';
@computed({ keepAlive: true }) get renderScriptingBox() {
TraceMobx();
return (
@@ -583,8 +592,8 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
placeholder="write your script here"
onFocus={this.onFocus}
onBlur={() => this._overlayDisposer?.()}
- onChange={(e: any) => (this.rawScript = e.target.value)}
- value={this.rawScript}
+ onChange={action((e: any) => (this.rawText = e.target.value))}
+ value={this.rawText}
movePopupAsYouType={true}
loadingComponent={() => <span>Loading</span>}
trigger={{
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index b1f7d8023..a3d501153 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -418,31 +418,26 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// extracts video thumbnails and saves them as field of doc
getVideoThumbnails = () => {
- const video = document.createElement('video');
+ if (this.dataDoc.thumbnails !== undefined) return;
+ this.dataDoc.thumbnails = new List<string>();
const thumbnailPromises: Promise<any>[] = [];
+ const video = document.createElement('video');
- video.onloadedmetadata = () => {
- video.currentTime = 0;
- };
+ video.onloadedmetadata = () => (video.currentTime = 0);
video.onseeked = () => {
const canvas = document.createElement('canvas');
- canvas.height = video.videoHeight;
- canvas.width = video.videoWidth;
- const ctx = canvas.getContext('2d');
- ctx?.drawImage(video, 0, 0, canvas.width, canvas.height);
- const imgUrl = canvas.toDataURL();
+ canvas.height = 100;
+ canvas.width = 100;
+ canvas.getContext('2d')?.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, 100, 100);
const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, '');
const encodedFilename = encodeURIComponent('thumbnail' + retitled + '_' + video.currentTime.toString().replace(/\./, '_'));
- const filename = basename(encodedFilename);
- thumbnailPromises.push(VideoBox.convertDataUri(imgUrl, filename));
+ thumbnailPromises?.push(VideoBox.convertDataUri(canvas.toDataURL(), basename(encodedFilename), true));
const newTime = video.currentTime + video.duration / (VideoBox.numThumbnails - 1);
if (newTime < video.duration) {
video.currentTime = newTime;
} else {
- Promise.all(thumbnailPromises).then(thumbnails => {
- this.dataDoc.thumbnails = new List<string>(thumbnails);
- });
+ Promise.all(thumbnailPromises).then(thumbnails => (this.dataDoc.thumbnails = new List<string>(thumbnails)));
}
};
@@ -898,7 +893,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content];
- scaling = () => this.props.scaling?.() || 1;
+ scaling = () => this.props.NativeDimScaling?.() || 1;
panelWidth = () => (this.props.PanelWidth() * this.heightPercent) / 100;
panelHeight = () => (this.layoutDoc._fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : (this.props.PanelHeight() * this.heightPercent) / 100);
@@ -911,7 +906,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
.scale(100 / this.heightPercent);
};
- marqueeFitScaling = () => ((this.props.scaling?.() || 1) * this.heightPercent) / 100;
+ marqueeFitScaling = () => ((this.props.NativeDimScaling?.() || 1) * this.heightPercent) / 100;
marqueeOffset = () => [((this.panelWidth() / 2) * (1 - this.heightPercent / 100)) / (this.heightPercent / 100), 0];
timelineDocFilter = () => [`_timelineLabel:true,${Utils.noRecursionHack}:x`];
@@ -1124,7 +1119,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
ScreenToLocalTransform={this.screenToLocalTransform}
docFilters={this.timelineDocFilter}
select={emptyFunction}
- scaling={returnOne}
+ NativeDimScaling={returnOne}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
removeDocument={this.removeDocument}
moveDocument={this.moveDocument}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index d97277c2b..ca9f363c1 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -15,7 +15,7 @@ import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupM
import { Docs, DocUtils } from '../../documents/Documents';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SnappingManager } from '../../util/SnappingManager';
-import { undoBatch } from '../../util/UndoManager';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
import { MarqueeOptionsMenu } from '../collections/collectionFreeForm';
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { ContextMenu } from '../ContextMenu';
@@ -134,25 +134,30 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
};
- lockout = false;
updateThumb = async () => {
const imageBitmap = ImageCast(this.layoutDoc['thumb-frozen'])?.url.href;
const scrollTop = NumCast(this.layoutDoc._scrollTop);
const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
const nativeHeight = (nativeWidth * this.props.PanelHeight()) / this.props.PanelWidth();
- if (!this.lockout && this._iframe && !imageBitmap && (scrollTop !== this.layoutDoc.thumbScrollTop || nativeWidth !== this.layoutDoc.thumbNativeWidth || nativeHeight !== this.layoutDoc.thumbNativeHeight)) {
+ if (
+ !this.rootDoc.thumbLockout &&
+ !this.props.dontRegisterView &&
+ this._iframe &&
+ !imageBitmap &&
+ (scrollTop !== this.layoutDoc.thumbScrollTop || nativeWidth !== this.layoutDoc.thumbNativeWidth || nativeHeight !== this.layoutDoc.thumbNativeHeight)
+ ) {
var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument);
if (!htmlString) {
htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text();
}
this.layoutDoc.thumb = undefined;
- this.lockout = true; // lock to prevent multiple thumb updates.
+ this.rootDoc.thumbLockout = true; // lock to prevent multiple thumb updates.
CreateImage(this._webUrl.endsWith('/') ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl, this._iframe.contentDocument?.styleSheets ?? [], htmlString, nativeWidth, nativeHeight, scrollTop)
.then((data_url: any) => {
VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + '-icon' + new Date().getTime(), true, this.layoutDoc[Id] + '-icon').then(returnedfilename =>
setTimeout(
action(() => {
- this.lockout = false;
+ this.rootDoc.thumbLockout = false;
this.layoutDoc.thumb = new ImageField(returnedfilename);
this.layoutDoc.thumbScrollTop = scrollTop;
this.layoutDoc.thumbNativeWidth = nativeWidth;
@@ -200,7 +205,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
autoHeight => {
if (autoHeight) {
this.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']);
- this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']) * (this.props.scaling?.() || 1));
+ this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']) * (this.props.NativeDimScaling?.() || 1));
}
}
);
@@ -276,7 +281,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
if (doc !== this.rootDoc && this._outerRef.current) {
- const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
+ const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(doc.y) + doc[HeightSym](), this.getScrollHeight()));
if (scrollTo !== undefined && this._initialScroll === undefined) {
const focusSpeed = smooth ? 500 : 0;
@@ -310,7 +315,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this.props.docViewPath().lastElement()?.docView?.cleanupPointerEvents(); // pointerup events aren't generated on containing document view, so we have to invoke it here.
if (this._iframe?.contentWindow && this._iframe.contentDocument && !this._iframe.contentWindow.getSelection()?.isCollapsed) {
const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
- const scale = (this.props.scaling?.() || 1) * mainContBounds.scale;
+ const scale = (this.props.NativeDimScaling?.() || 1) * mainContBounds.scale;
const sel = this._iframe.contentWindow.getSelection();
if (sel) {
this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined);
@@ -322,7 +327,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
iframeDown = (e: PointerEvent) => {
const sel = this._iframe?.contentWindow?.getSelection?.();
const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
- const scale = (this.props.scaling?.() || 1) * mainContBounds.scale;
+ const scale = (this.props.NativeDimScaling?.() || 1) * mainContBounds.scale;
const word = getWordAtPoint(e.target, e.clientX, e.clientY);
this._setPreviewCursor?.(e.clientX, e.clientY, false, true);
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
@@ -604,7 +609,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
funcs.push({
description: (!this.layoutDoc.forceReflow ? 'Force' : 'Prevent') + ' Reflow',
event: () => {
- const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.scaling?.() || 1);
+ const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.NativeDimScaling?.() || 1);
this.layoutDoc.forceReflow = !nw;
if (nw) {
Doc.SetInPlace(this.layoutDoc, this.fieldKey + '-nativeWidth', nw, true);
@@ -708,6 +713,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
};
@observable _draggingSidebar = false;
sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => {
+ const batch = UndoManager.StartBatch('sidebar');
// onButton determines whether the width of the pdf box changes, or just the ratio of the sidebar to the pdf
setupMoveUpEvents(
this,
@@ -716,12 +722,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this._draggingSidebar = true;
const localDelta = this.props
.ScreenToLocalTransform()
- .scale(this.props.scaling?.() || 1)
+ .scale(this.props.NativeDimScaling?.() || 1)
.transformDirection(delta[0], delta[1]);
const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
const nativeHeight = NumCast(this.layoutDoc[this.fieldKey + '-nativeHeight']);
const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
- const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.scaling?.() || 1)) / nativeWidth;
+ const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.NativeDimScaling?.() || 1)) / nativeWidth;
if (ratio >= 1) {
this.layoutDoc.nativeWidth = nativeWidth * ratio;
this.layoutDoc.nativeHeight = nativeHeight * (1 + ratio);
@@ -730,10 +736,32 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
return false;
}),
- action(() => (this._draggingSidebar = false)),
- () => this.toggleSidebar()
+ action((e, movement, isClick) => {
+ this._draggingSidebar = false;
+ !isClick && batch.end();
+ }),
+ () => {
+ this.toggleSidebar();
+ batch.end();
+ }
);
};
+ @computed get sidebarHandle() {
+ return (
+ <div
+ className="webBox-overlayButton-sidebar"
+ key="sidebar"
+ title="Toggle Sidebar"
+ style={{
+ display: !this.props.isContentActive() ? 'none' : undefined,
+ top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
+ backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
+ }}
+ onPointerDown={e => this.sidebarBtnDown(e, true)}>
+ <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
+ </div>
+ );
+ }
@observable _previewNativeWidth: Opt<number> = undefined;
@observable _previewWidth: Opt<number> = undefined;
toggleSidebar = action((preview: boolean = false) => {
@@ -827,8 +855,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => (this._searchString = e.currentTarget.value);
showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
- panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth; // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
- panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
+ panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth; // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
+ panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter('textInlineAnnotations')];
@@ -845,7 +873,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
render() {
const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
- const scale = previewScale * (this.props.scaling?.() || 1);
+ const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
const renderAnnotations = (docFilters?: () => string[]) => (
<CollectionFreeFormView
{...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
@@ -857,12 +885,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
PanelWidth={this.panelWidth}
PanelHeight={this.panelHeight}
ScreenToLocalTransform={this.scrollXf}
- scaling={returnOne}
+ NativeDimScaling={returnOne}
dropAction={'alias'}
docFilters={docFilters || this.basicFilter}
dontRenderDocuments={docFilters ? false : true}
select={emptyFunction}
- ContentScaling={returnOne}
bringToFront={emptyFunction}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
removeDocument={this.removeDocument}
@@ -934,33 +961,24 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}}
onPointerDown={e => this.sidebarBtnDown(e, false)}
/>
- <SidebarAnnos
- ref={this._sidebarRef}
- {...this.props}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- fieldKey={this.fieldKey + '-' + this._urlHash}
- rootDoc={this.rootDoc}
- layoutDoc={this.layoutDoc}
- dataDoc={this.dataDoc}
- setHeight={emptyFunction}
- nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth) - WebBox.sidebarResizerWidth / (this.props.scaling?.() || 1)}
- showSidebar={this.SidebarShown}
- sidebarAddDocument={this.sidebarAddDocument}
- moveDocument={this.moveDocument}
- removeDocument={this.removeDocument}
- />
- <div
- className="webBox-overlayButton-sidebar"
- key="sidebar"
- title="Toggle Sidebar"
- style={{
- display: !this.props.isContentActive() ? 'none' : undefined,
- top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
- backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
- }}
- onPointerDown={e => this.sidebarBtnDown(e, true)}>
- <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
+ <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.layoutDoc[WidthSym]()}%` }}>
+ <SidebarAnnos
+ ref={this._sidebarRef}
+ {...this.props}
+ whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+ fieldKey={this.fieldKey + '-' + this._urlHash}
+ rootDoc={this.rootDoc}
+ layoutDoc={this.layoutDoc}
+ dataDoc={this.dataDoc}
+ setHeight={emptyFunction}
+ nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth) - WebBox.sidebarResizerWidth / (this.props.NativeDimScaling?.() || 1)}
+ showSidebar={this.SidebarShown}
+ sidebarAddDocument={this.sidebarAddDocument}
+ moveDocument={this.moveDocument}
+ removeDocument={this.removeDocument}
+ />
</div>
+ {this.sidebarHandle}
{!this.props.isContentActive() ? null : this.searchUI}
</div>
);
diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss
index 6cd56f84e..a1ca777b3 100644
--- a/src/client/views/nodes/button/FontIconBox.scss
+++ b/src/client/views/nodes/button/FontIconBox.scss
@@ -1,4 +1,4 @@
-@import "../../global/globalCssVariables";
+@import '../../global/globalCssVariables';
.menuButton {
height: 100%;
@@ -31,8 +31,6 @@
text-transform: uppercase;
font-weight: bold;
transition: 0.15s;
-
-
}
.fontIconBox-icon {
@@ -106,31 +104,31 @@
right: 0;
bottom: 0;
background-color: lightgrey;
- -webkit-transition: .4s;
- transition: .4s;
+ -webkit-transition: 0.4s;
+ transition: 0.4s;
}
.slider:before {
position: absolute;
- content: "";
+ content: '';
height: 21px;
width: 21px;
left: 2px;
bottom: 2px;
background-color: $white;
- -webkit-transition: .4s;
- transition: .4s;
+ -webkit-transition: 0.4s;
+ transition: 0.4s;
}
- input:checked+.slider {
+ input:checked + .slider {
background-color: $medium-blue;
}
- input:focus+.slider {
+ input:focus + .slider {
box-shadow: 0 0 1px $medium-blue;
}
- input:checked+.slider:before {
+ input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
@@ -197,8 +195,6 @@
}
}
-
-
&.colorBtn,
&.colorBtnLabel {
color: black;
@@ -311,18 +307,21 @@
box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
border-radius: $standard-border-radius;
- input[type=range]::-webkit-slider-runnable-track {
+ .menu-slider {
+ height: 10px;
+ }
+ input[type='range']::-webkit-slider-runnable-track {
background: gray;
height: 3px;
}
- input[type=range]::-webkit-slider-thumb {
+ input[type='range']::-webkit-slider-thumb {
box-shadow: 1px 1px 1px #000000;
border: 1px solid #000000;
height: 10px;
width: 10px;
border-radius: 5px;
- background: #FFFFFF;
+ background: #ffffff;
cursor: pointer;
-webkit-appearance: none;
margin-top: -4px;
@@ -456,5 +455,4 @@
background: transparent;
position: fixed;
}
-
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 1ce97979e..d3b95e25a 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -1,7 +1,7 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
-import { action, computed, observable } from 'mobx';
+import { action, computed, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ColorState, SketchPicker } from 'react-color';
@@ -86,11 +86,21 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
}
// Determining UI Specs
- @observable private label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
- @observable private icon = StrCast(this.dataDoc.icon, 'user') as any;
- @observable private dropdown: boolean = BoolCast(this.rootDoc.dropDownOpen);
- @observable private buttonList: string[] = StrListCast(this.rootDoc.btnList);
- @observable private type = StrCast(this.rootDoc.btnType);
+ @computed get label() {
+ return StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
+ }
+ @computed get icon() {
+ return StrCast(this.dataDoc.icon, 'user') as any;
+ }
+ @computed get dropdown() {
+ return BoolCast(this.rootDoc.dropDownOpen);
+ }
+ @computed get buttonList() {
+ return StrListCast(this.rootDoc.btnList);
+ }
+ @computed get type() {
+ return StrCast(this.rootDoc.btnType);
+ }
/**
* Types of buttons in dash:
@@ -569,11 +579,11 @@ ScriptingGlobals.add(function setView(view: string) {
// toggle: Set overlay status of selected document
ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) {
- const selected = SelectionManager.Docs().lastElement();
+ const selected = SelectionManager.Views().lastElement();
if (checkResult) {
- return selected?._backgroundColor ?? 'transparent';
+ return selected?.props.Document._backgroundColor ?? 'transparent';
}
- if (selected) selected._backgroundColor = color;
+ if (selected) selected.props.Document._backgroundColor = color;
});
// toggle: Set overlay status of selected document
@@ -700,6 +710,13 @@ ScriptingGlobals.add(function toggleNoAutoLinkAnchor(checkResult?: boolean) {
}
if (editorView) RichTextMenu.Instance?.toggleNoAutoLinkAnchor();
});
+ScriptingGlobals.add(function toggleDictation(checkResult?: boolean) {
+ const textView = RichTextMenu.Instance?.TextView;
+ if (checkResult) {
+ return textView?._recording ? Colors.MEDIUM_BLUE : 'transparent';
+ }
+ if (textView) runInAction(() => (textView._recording = !textView._recording));
+});
ScriptingGlobals.add(function toggleBold(checkResult?: boolean) {
const editorView = RichTextMenu.Instance?.TextView?.EditorView;
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 08f255cab..73a711b9d 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -70,6 +70,8 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
@observable _dashDoc: Doc | undefined;
@observable _finalLayout: any;
@observable _resolvedDataDoc: any;
+ @observable _width: number = 0;
+ @observable _height: number = 0;
updateDoc = action((dashDoc: Doc) => {
this._dashDoc = dashDoc;
@@ -83,12 +85,14 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
}
if (this.props.width !== (this._dashDoc?._width ?? '') + 'px' || this.props.height !== (this._dashDoc?._height ?? '') + 'px') {
try {
+ this._width = NumCast(this._dashDoc?._width);
+ this._height = NumCast(this._dashDoc?._height);
// bcz: an exception will be thrown if two aliases are open at the same time when a doc view comment is made
this.props.view.dispatch(
this.props.view.state.tr.setNodeMarkup(this.props.getPos(), null, {
...this.props.node.attrs,
- width: (this._dashDoc?._width ?? '') + 'px',
- height: (this._dashDoc?._height ?? '') + 'px',
+ width: this._width + 'px',
+ height: this._height + 'px',
})
);
} catch (e) {
@@ -121,17 +125,17 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
componentDidMount() {
this._disposers.upater = reaction(
() => this._dashDoc && NumCast(this._dashDoc._height) + NumCast(this._dashDoc._width),
- () => {
+ action(() => {
if (this._dashDoc) {
- this.props.view.dispatch(
- this.props.view.state.tr.setNodeMarkup(this.props.getPos(), null, {
- ...this.props.node.attrs,
- width: (this._dashDoc?._width ?? '') + 'px',
- height: (this._dashDoc?._height ?? '') + 'px',
- })
- );
+ this._width = NumCast(this._dashDoc._width);
+ this._height = NumCast(this._dashDoc._height);
+ const parent = this._spanRef.current?.parentNode as HTMLElement;
+ if (parent) {
+ parent.style.width = this._width + 'px';
+ parent.style.height = this._height + 'px';
+ }
}
- }
+ })
);
}
@@ -172,8 +176,8 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
ref={this._spanRef}
className="dash-span"
style={{
- width: this.props.width,
- height: this.props.height,
+ width: this._width,
+ height: this._height,
position: 'absolute',
display: 'inline-block',
}}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 8c8b74560..d2f7b5677 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -216,7 +216,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
onPointerDownLabelSpan = (e: any) => {
setupMoveUpEvents(this, e, returnFalse, returnFalse, e => {
DashFieldViewMenu.createFieldView = this.createPivotForField;
- DashFieldViewMenu.Instance.show(e.clientX, e.clientY + 16);
+ DashFieldViewMenu.Instance.show(e.clientX, e.clientY + 16, this._fieldKey);
});
};
@@ -253,17 +253,21 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
DashFieldViewMenu.Instance.fadeOut(true);
};
- public show = (x: number, y: number) => {
+ @observable _fieldKey = '';
+
+ @action
+ public show = (x: number, y: number, fieldKey: string) => {
+ this._fieldKey = fieldKey;
this.jumpTo(x, y, true);
const hideMenu = () => {
this.fadeOut(true);
- document.removeEventListener('pointerdown', hideMenu);
+ document.removeEventListener('pointerdown', hideMenu, true);
};
- document.addEventListener('pointerdown', hideMenu);
+ document.addEventListener('pointerdown', hideMenu, true);
};
render() {
const buttons = [
- <Tooltip key="trash" title={<div className="dash-tooltip">{'Remove Link Anchor'}</div>}>
+ <Tooltip key="trash" title={<div className="dash-tooltip">{`Show Pivot Viewer for '${this._fieldKey}'`}</div>}>
<button className="antimodeMenu-button" onPointerDown={this.showFields}>
<FontAwesomeIcon icon="eye" size="lg" />
</button>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 27817f317..d3d8c47c0 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -1,4 +1,4 @@
-@import "../../global/globalCssVariables";
+@import '../../global/globalCssVariables';
.ProseMirror {
width: 100%;
@@ -11,13 +11,13 @@
}
audiotag {
- left: 0;
- position: absolute;
- cursor: pointer;
- border-radius: 10px;
- width: 10px;
- margin-top: -2px;
- font-size: 4px;
+ left: 0;
+ position: absolute;
+ cursor: pointer;
+ border-radius: 10px;
+ width: 10px;
+ margin-top: -2px;
+ font-size: 4px;
background: lightblue;
}
audiotag:hover {
@@ -63,12 +63,11 @@ audiotag:hover {
.formattedTextBox-outer-selected {
cursor: text;
}
-
+
.formattedTextBox-sidebar-handle {
position: absolute;
top: 0;
- left: 17px;
- //top: calc(50% - 17.5px); // use this to center vertically -- make sure it looks okay for slide views
+ right: 0;
width: 17px;
height: 17px;
font-size: 11px;
@@ -79,15 +78,14 @@ audiotag:hover {
display: flex;
justify-content: center;
align-items: center;
- cursor:grabbing;
+ cursor: grabbing;
box-shadow: $standard-box-shadow;
// transition: 0.2s;
opacity: 0.3;
- &:hover{
+ &:hover {
opacity: 1 !important;
filter: brightness(0.85);
}
-
}
.formattedTextBox-sidebar,
@@ -117,14 +115,15 @@ audiotag:hover {
left: 10%;
}
-.formattedTextBox-inner-rounded, .formattedTextBox-inner-rounded-selected,
-.formattedTextBox-inner,
+.formattedTextBox-inner-rounded,
+.formattedTextBox-inner-rounded-selected,
+.formattedTextBox-inner,
.formattedTextBox-inner-minimal,
.formattedTextBox-inner-selected {
height: 100%;
white-space: pre-wrap;
.ProseMirror:hover {
- background: rgba(200,200,200,0.2);
+ background: rgba(200, 200, 200, 0.2);
}
hr {
display: block;
@@ -141,7 +140,7 @@ audiotag:hover {
.formattedTextBox-inner-rounded-selected,
.formattedTextBox-inner-selected {
> .ProseMirror {
- padding:10px;
+ padding: 10px;
}
}
.formattedTextBox-outer-selected {
@@ -236,18 +235,17 @@ footnote::after {
position: absolute;
top: -5px;
left: 27px;
- content: " ";
+ content: ' ';
height: 0;
width: 0;
}
-
.formattedTextBox-inlineComment {
position: relative;
width: 40px;
height: 20px;
&::before {
- content: "→";
+ content: '→';
}
&:hover {
background: orange;
@@ -260,7 +258,7 @@ footnote::after {
width: 40px;
height: 20px;
&::after {
- content: "←";
+ content: '←';
}
}
@@ -270,21 +268,21 @@ footnote::after {
width: 40px;
height: 20px;
&::after {
- content: "...";
+ content: '...';
}
}
.prosemirror-anchor {
- overflow:hidden;
- display:inline-grid;
+ overflow: hidden;
+ display: inline-grid;
}
.prosemirror-linkBtn {
- background:unset;
- color:unset;
- padding:0;
+ background: unset;
+ color: unset;
+ padding: 0;
text-transform: unset;
letter-spacing: unset;
- font-size:unset;
+ font-size: unset;
}
.prosemirror-links {
display: none;
@@ -294,28 +292,28 @@ footnote::after {
z-index: 1;
padding: 5;
border-radius: 2px;
- }
- .prosemirror-hrefoptions{
- width:0px;
- border:unset;
- padding:0px;
- }
-
- .prosemirror-links a {
+}
+.prosemirror-hrefoptions {
+ width: 0px;
+ border: unset;
+ padding: 0px;
+}
+
+.prosemirror-links a {
float: left;
color: white;
text-decoration: none;
border-radius: 3px;
- }
+}
- .prosemirror-links a:hover {
+.prosemirror-links a:hover {
background-color: #eee;
color: black;
- }
+}
- .prosemirror-anchor:hover .prosemirror-links {
+.prosemirror-anchor:hover .prosemirror-links {
display: grid;
- }
+}
.ProseMirror {
padding: 0px;
@@ -334,7 +332,8 @@ footnote::after {
border-left: solid 2px dimgray;
}
- ol, ul {
+ ol,
+ ul {
counter-reset: deci1 0 multi1 0;
padding-left: 1em;
font-family: inherit;
@@ -342,42 +341,231 @@ footnote::after {
ol {
font-family: inherit;
}
- .bullet { p { font-family: inherit} margin-left: 0; }
- .bullet1 { p { font-family: inherit} }
- .bullet2,.bullet3,.bullet4,.bullet5,.bullet6 { p { font-family: inherit} font-size: smaller; }
+ .bullet {
+ p {
+ font-family: inherit;
+ }
+ margin-left: 0;
+ }
+ .bullet1 {
+ p {
+ font-family: inherit;
+ }
+ }
+ .bullet2,
+ .bullet3,
+ .bullet4,
+ .bullet5,
+ .bullet6 {
+ p {
+ font-family: inherit;
+ }
+ font-size: smaller;
+ }
- .decimal1-ol { counter-reset: deci1; p {display: inline-block; font-family: inherit} margin-left: 0; }
- .decimal2-ol { counter-reset: deci2; p {display: inline-block; font-family: inherit} font-size: smaller; padding-left: 2.1em;}
- .decimal3-ol { counter-reset: deci3; p {display: inline-block; font-family: inherit} font-size: smaller; padding-left: 2.85em;}
- .decimal4-ol { counter-reset: deci4; p {display: inline-block; font-family: inherit} font-size: smaller; padding-left: 3.85em;}
- .decimal5-ol { counter-reset: deci5; p {display: inline-block; font-family: inherit} font-size: smaller; }
- .decimal6-ol { counter-reset: deci6; p {display: inline-block; font-family: inherit} font-size: smaller; }
- .decimal7-ol { counter-reset: deci7; p {display: inline-block; font-family: inherit} font-size: smaller; }
+ .decimal1-ol {
+ counter-reset: deci1;
+ p {
+ display: inline-block;
+ font-family: inherit;
+ }
+ margin-left: 0;
+ }
+ .decimal2-ol {
+ counter-reset: deci2;
+ p {
+ display: inline-block;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ padding-left: 2.1em;
+ }
+ .decimal3-ol {
+ counter-reset: deci3;
+ p {
+ display: inline-block;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ padding-left: 2.85em;
+ }
+ .decimal4-ol {
+ counter-reset: deci4;
+ p {
+ display: inline-block;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ padding-left: 3.85em;
+ }
+ .decimal5-ol {
+ counter-reset: deci5;
+ p {
+ display: inline-block;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ }
+ .decimal6-ol {
+ counter-reset: deci6;
+ p {
+ display: inline-block;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ }
+ .decimal7-ol {
+ counter-reset: deci7;
+ p {
+ display: inline-block;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ }
- .multi1-ol { counter-reset: multi1; p {display: inline-block; font-family: inherit} margin-left: 0; padding-left: 1.2em }
- .multi2-ol { counter-reset: multi2; p {display: inline-block; font-family: inherit} font-size: smaller; padding-left: 2em;}
- .multi3-ol { counter-reset: multi3; p {display: inline-block; font-family: inherit} font-size: smaller; padding-left: 2.85em;}
- .multi4-ol { counter-reset: multi4; p {display: inline-block; font-family: inherit} font-size: smaller; padding-left: 3.85em;}
+ .multi1-ol {
+ counter-reset: multi1;
+ p {
+ display: inline-block;
+ font-family: inherit;
+ }
+ margin-left: 0;
+ padding-left: 1.2em;
+ }
+ .multi2-ol {
+ counter-reset: multi2;
+ p {
+ display: inline-block;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ padding-left: 2em;
+ }
+ .multi3-ol {
+ counter-reset: multi3;
+ p {
+ display: inline-block;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ padding-left: 2.85em;
+ }
+ .multi4-ol {
+ counter-reset: multi4;
+ p {
+ display: inline-block;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ padding-left: 3.85em;
+ }
//.bullet:before, .bullet1:before, .bullet2:before, .bullet3:before, .bullet4:before, .bullet5:before { transition: 0.5s; display: inline-block; vertical-align: top; margin-left: -1em; width: 1em; content:" " }
- .decimal1:before { transition: 0.5s;counter-increment: deci1; display: inline-block; vertical-align: top; margin-left: -1em; width: 1em; content: counter(deci1) ". "; }
- .decimal2:before { transition: 0.5s;counter-increment: deci2; display: inline-block; vertical-align: top; margin-left: -2.1em; width: 2.1em; content: counter(deci1) "."counter(deci2) ". "; }
- .decimal3:before { transition: 0.5s;counter-increment: deci3; display: inline-block; vertical-align: top; margin-left: -2.85em;width: 2.85em; content: counter(deci1) "."counter(deci2) "."counter(deci3) ". "; }
- .decimal4:before { transition: 0.5s;counter-increment: deci4; display: inline-block; vertical-align: top; margin-left: -3.85em;width: 3.85em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) ". "; }
- .decimal5:before { transition: 0.5s;counter-increment: deci5; display: inline-block; vertical-align: top; margin-left: -2em; width: 5em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) ". "; }
- .decimal6:before { transition: 0.5s;counter-increment: deci6; display: inline-block; vertical-align: top; margin-left: -2em; width: 6em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) ". "; }
- .decimal7:before { transition: 0.5s;counter-increment: deci7; display: inline-block; vertical-align: top; margin-left: -2em; width: 7em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) "."counter(deci7) ". "; }
+ .decimal1:before {
+ transition: 0.5s;
+ counter-increment: deci1;
+ display: inline-block;
+ vertical-align: top;
+ margin-left: -1em;
+ width: 1em;
+ content: counter(deci1) '. ';
+ }
+ .decimal2:before {
+ transition: 0.5s;
+ counter-increment: deci2;
+ display: inline-block;
+ vertical-align: top;
+ margin-left: -2.1em;
+ width: 2.1em;
+ content: counter(deci1) '.' counter(deci2) '. ';
+ }
+ .decimal3:before {
+ transition: 0.5s;
+ counter-increment: deci3;
+ display: inline-block;
+ vertical-align: top;
+ margin-left: -2.85em;
+ width: 2.85em;
+ content: counter(deci1) '.' counter(deci2) '.' counter(deci3) '. ';
+ }
+ .decimal4:before {
+ transition: 0.5s;
+ counter-increment: deci4;
+ display: inline-block;
+ vertical-align: top;
+ margin-left: -3.85em;
+ width: 3.85em;
+ content: counter(deci1) '.' counter(deci2) '.' counter(deci3) '.' counter(deci4) '. ';
+ }
+ .decimal5:before {
+ transition: 0.5s;
+ counter-increment: deci5;
+ display: inline-block;
+ vertical-align: top;
+ margin-left: -2em;
+ width: 5em;
+ content: counter(deci1) '.' counter(deci2) '.' counter(deci3) '.' counter(deci4) '.' counter(deci5) '. ';
+ }
+ .decimal6:before {
+ transition: 0.5s;
+ counter-increment: deci6;
+ display: inline-block;
+ vertical-align: top;
+ margin-left: -2em;
+ width: 6em;
+ content: counter(deci1) '.' counter(deci2) '.' counter(deci3) '.' counter(deci4) '.' counter(deci5) '.' counter(deci6) '. ';
+ }
+ .decimal7:before {
+ transition: 0.5s;
+ counter-increment: deci7;
+ display: inline-block;
+ vertical-align: top;
+ margin-left: -2em;
+ width: 7em;
+ content: counter(deci1) '.' counter(deci2) '.' counter(deci3) '.' counter(deci4) '.' counter(deci5) '.' counter(deci6) '.' counter(deci7) '. ';
+ }
- .multi1:before { transition: 0.5s;counter-increment: multi1; display: inline-block; vertical-align: top; margin-left: -1.3em; width: 1.2em; content: counter(multi1, upper-alpha) ". "; }
- .multi2:before { transition: 0.5s;counter-increment: multi2; display: inline-block; vertical-align: top; margin-left: -2em; width: 2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) ". "; }
- .multi3:before { transition: 0.5s;counter-increment: multi3; display: inline-block; vertical-align: top; margin-left: -2.85em; width:2.85em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) ". "; }
- .multi4:before { transition: 0.5s;counter-increment: multi4; display: inline-block; vertical-align: top; margin-left: -4.2em; width: 4.2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) "."counter(multi4, lower-roman) ". "; }
+ .multi1:before {
+ transition: 0.5s;
+ counter-increment: multi1;
+ display: inline-block;
+ vertical-align: top;
+ margin-left: -1.3em;
+ width: 1.2em;
+ content: counter(multi1, upper-alpha) '. ';
+ }
+ .multi2:before {
+ transition: 0.5s;
+ counter-increment: multi2;
+ display: inline-block;
+ vertical-align: top;
+ margin-left: -2em;
+ width: 2em;
+ content: counter(multi1, upper-alpha) '.' counter(multi2, decimal) '. ';
+ }
+ .multi3:before {
+ transition: 0.5s;
+ counter-increment: multi3;
+ display: inline-block;
+ vertical-align: top;
+ margin-left: -2.85em;
+ width: 2.85em;
+ content: counter(multi1, upper-alpha) '.' counter(multi2, decimal) '.' counter(multi3, lower-alpha) '. ';
+ }
+ .multi4:before {
+ transition: 0.5s;
+ counter-increment: multi4;
+ display: inline-block;
+ vertical-align: top;
+ margin-left: -4.2em;
+ width: 4.2em;
+ content: counter(multi1, upper-alpha) '.' counter(multi2, decimal) '.' counter(multi3, lower-alpha) '.' counter(multi4, lower-roman) '. ';
+ }
}
-
@media only screen and (max-width: 1000px) {
- @import "../../global/globalCssVariables";
+ @import '../../global/globalCssVariables';
.ProseMirror {
width: 100%;
@@ -425,7 +613,7 @@ footnote::after {
width: 100%;
height: 100%;
}
-
+
.formattedTextBox-sidebar-handle {
position: absolute;
background: lightgray;
@@ -562,18 +750,17 @@ footnote::after {
position: absolute;
top: -5px;
left: 27px;
- content: " ";
+ content: ' ';
height: 0;
width: 0;
}
-
.formattedTextBox-inlineComment {
position: relative;
width: 40px;
height: 20px;
&::before {
- content: "→";
+ content: '→';
}
&:hover {
background: orange;
@@ -586,7 +773,7 @@ footnote::after {
width: 40px;
height: 20px;
&::after {
- content: "←";
+ content: '←';
}
}
@@ -596,7 +783,7 @@ footnote::after {
width: 40px;
height: 20px;
&::after {
- content: "...";
+ content: '...';
}
}
@@ -606,7 +793,8 @@ footnote::after {
font-family: inherit;
}
- ol, ul {
+ ol,
+ ul {
counter-reset: deci1 0 multi1 0;
padding-left: 1em;
font-family: inherit;
@@ -616,30 +804,191 @@ footnote::after {
font-family: inherit;
}
- .decimal1-ol { counter-reset: deci1; p {display: inline; font-family: inherit} margin-left: 0; }
- .decimal2-ol { counter-reset: deci2; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 1em;}
- .decimal3-ol { counter-reset: deci3; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 2em;}
- .decimal4-ol { counter-reset: deci4; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 3em;}
- .decimal5-ol { counter-reset: deci5; p {display: inline; font-family: inherit} font-size: smaller; }
- .decimal6-ol { counter-reset: deci6; p {display: inline; font-family: inherit} font-size: smaller; }
- .decimal7-ol { counter-reset: deci7; p {display: inline; font-family: inherit} font-size: smaller; }
-
- .multi1-ol { counter-reset: multi1; p {display: inline; font-family: inherit} margin-left: 0; padding-left: 1.2em }
- .multi2-ol { counter-reset: multi2; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 1.4em;}
- .multi3-ol { counter-reset: multi3; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 2em;}
- .multi4-ol { counter-reset: multi4; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 3.4em;}
-
- .decimal1:before { transition: 0.5s;counter-increment: deci1; display: inline-block; margin-left: -1em; width: 1em; content: counter(deci1) ". "; }
- .decimal2:before { transition: 0.5s;counter-increment: deci2; display: inline-block; margin-left: -2.1em; width: 2.1em; content: counter(deci1) "."counter(deci2) ". "; }
- .decimal3:before { transition: 0.5s;counter-increment: deci3; display: inline-block; margin-left: -2.85em;width: 2.85em; content: counter(deci1) "."counter(deci2) "."counter(deci3) ". "; }
- .decimal4:before { transition: 0.5s;counter-increment: deci4; display: inline-block; margin-left: -3.85em;width: 3.85em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) ". "; }
- .decimal5:before { transition: 0.5s;counter-increment: deci5; display: inline-block; margin-left: -2em; width: 5em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) ". "; }
- .decimal6:before { transition: 0.5s;counter-increment: deci6; display: inline-block; margin-left: -2em; width: 6em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) ". "; }
- .decimal7:before { transition: 0.5s;counter-increment: deci7; display: inline-block; margin-left: -2em; width: 7em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) "."counter(deci7) ". "; }
-
- .multi1:before { transition: 0.5s;counter-increment: multi1; display: inline-block; margin-left: -1em; width: 1.2em; content: counter(multi1, upper-alpha) ". "; }
- .multi2:before { transition: 0.5s;counter-increment: multi2; display: inline-block; margin-left: -2em; width: 2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) ". "; }
- .multi3:before { transition: 0.5s;counter-increment: multi3; display: inline-block; margin-left: -2.85em; width:2.85em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) ". "; }
- .multi4:before { transition: 0.5s;counter-increment: multi4; display: inline-block; margin-left: -4.2em; width: 4.2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) "."counter(multi4, lower-roman) ". "; }
+ .decimal1-ol {
+ counter-reset: deci1;
+ p {
+ display: inline;
+ font-family: inherit;
+ }
+ margin-left: 0;
+ }
+ .decimal2-ol {
+ counter-reset: deci2;
+ p {
+ display: inline;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ padding-left: 1em;
+ }
+ .decimal3-ol {
+ counter-reset: deci3;
+ p {
+ display: inline;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ padding-left: 2em;
+ }
+ .decimal4-ol {
+ counter-reset: deci4;
+ p {
+ display: inline;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ padding-left: 3em;
+ }
+ .decimal5-ol {
+ counter-reset: deci5;
+ p {
+ display: inline;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ }
+ .decimal6-ol {
+ counter-reset: deci6;
+ p {
+ display: inline;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ }
+ .decimal7-ol {
+ counter-reset: deci7;
+ p {
+ display: inline;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ }
+
+ .multi1-ol {
+ counter-reset: multi1;
+ p {
+ display: inline;
+ font-family: inherit;
+ }
+ margin-left: 0;
+ padding-left: 1.2em;
+ }
+ .multi2-ol {
+ counter-reset: multi2;
+ p {
+ display: inline;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ padding-left: 1.4em;
+ }
+ .multi3-ol {
+ counter-reset: multi3;
+ p {
+ display: inline;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ padding-left: 2em;
+ }
+ .multi4-ol {
+ counter-reset: multi4;
+ p {
+ display: inline;
+ font-family: inherit;
+ }
+ font-size: smaller;
+ padding-left: 3.4em;
+ }
+
+ .decimal1:before {
+ transition: 0.5s;
+ counter-increment: deci1;
+ display: inline-block;
+ margin-left: -1em;
+ width: 1em;
+ content: counter(deci1) '. ';
+ }
+ .decimal2:before {
+ transition: 0.5s;
+ counter-increment: deci2;
+ display: inline-block;
+ margin-left: -2.1em;
+ width: 2.1em;
+ content: counter(deci1) '.' counter(deci2) '. ';
+ }
+ .decimal3:before {
+ transition: 0.5s;
+ counter-increment: deci3;
+ display: inline-block;
+ margin-left: -2.85em;
+ width: 2.85em;
+ content: counter(deci1) '.' counter(deci2) '.' counter(deci3) '. ';
+ }
+ .decimal4:before {
+ transition: 0.5s;
+ counter-increment: deci4;
+ display: inline-block;
+ margin-left: -3.85em;
+ width: 3.85em;
+ content: counter(deci1) '.' counter(deci2) '.' counter(deci3) '.' counter(deci4) '. ';
+ }
+ .decimal5:before {
+ transition: 0.5s;
+ counter-increment: deci5;
+ display: inline-block;
+ margin-left: -2em;
+ width: 5em;
+ content: counter(deci1) '.' counter(deci2) '.' counter(deci3) '.' counter(deci4) '.' counter(deci5) '. ';
+ }
+ .decimal6:before {
+ transition: 0.5s;
+ counter-increment: deci6;
+ display: inline-block;
+ margin-left: -2em;
+ width: 6em;
+ content: counter(deci1) '.' counter(deci2) '.' counter(deci3) '.' counter(deci4) '.' counter(deci5) '.' counter(deci6) '. ';
+ }
+ .decimal7:before {
+ transition: 0.5s;
+ counter-increment: deci7;
+ display: inline-block;
+ margin-left: -2em;
+ width: 7em;
+ content: counter(deci1) '.' counter(deci2) '.' counter(deci3) '.' counter(deci4) '.' counter(deci5) '.' counter(deci6) '.' counter(deci7) '. ';
+ }
+
+ .multi1:before {
+ transition: 0.5s;
+ counter-increment: multi1;
+ display: inline-block;
+ margin-left: -1em;
+ width: 1.2em;
+ content: counter(multi1, upper-alpha) '. ';
+ }
+ .multi2:before {
+ transition: 0.5s;
+ counter-increment: multi2;
+ display: inline-block;
+ margin-left: -2em;
+ width: 2em;
+ content: counter(multi1, upper-alpha) '.' counter(multi2, decimal) '. ';
+ }
+ .multi3:before {
+ transition: 0.5s;
+ counter-increment: multi3;
+ display: inline-block;
+ margin-left: -2.85em;
+ width: 2.85em;
+ content: counter(multi1, upper-alpha) '.' counter(multi2, decimal) '.' counter(multi3, lower-alpha) '. ';
+ }
+ .multi4:before {
+ transition: 0.5s;
+ counter-increment: multi4;
+ display: inline-block;
+ margin-left: -4.2em;
+ width: 4.2em;
+ content: counter(multi1, upper-alpha) '.' counter(multi2, decimal) '.' counter(multi3, lower-alpha) '.' counter(multi4, lower-roman) '. ';
+ }
}
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index cfaa428f9..f61533619 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -11,7 +11,7 @@ import { Fragment, Mark, Node, Slice } from 'prosemirror-model';
import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from 'prosemirror-state';
import { EditorView } from 'prosemirror-view';
import { DateField } from '../../../../fields/DateField';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DataSym, Doc, DocListCast, DocListCastAsync, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym } from '../../../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, AclReadonly, AclSelfEdit, DataSym, Doc, DocListCast, DocListCastAsync, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
import { InkTool } from '../../../../fields/InkField';
import { PrefetchProxy } from '../../../../fields/Proxy';
@@ -20,11 +20,11 @@ import { RichTextUtils } from '../../../../fields/RichTextUtils';
import { ComputedField } from '../../../../fields/ScriptField';
import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils';
import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
import { DocServer } from '../../../DocServer';
import { Docs, DocUtils } from '../../../documents/Documents';
-import { DocumentType } from '../../../documents/DocumentTypes';
+import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { DictationManager } from '../../../util/DictationManager';
import { DocumentManager } from '../../../util/DocumentManager';
import { DragManager } from '../../../util/DragManager';
@@ -61,6 +61,9 @@ import { schema } from './schema_rts';
import { SummaryView } from './SummaryView';
import applyDevTools = require('prosemirror-dev-tools');
import React = require('react');
+import { text } from 'body-parser';
+import { CollectionTreeView } from '../../collections/CollectionTreeView';
+import { DocumentViewInternal } from '../DocumentView';
const translateGoogleApi = require('translate-google-api');
export interface FormattedTextBoxProps {
@@ -123,6 +126,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
return DocListCast(this.dataDoc[this.SidebarKey]);
}
+ @computed get noSidebar() {
+ return this.props.docViewPath?.()[this.props.docViewPath().length - 2]?.rootDoc.type === DocumentType.RTF || this.props.noSidebar || this.Document._noSidebar;
+ }
@computed get sidebarWidthPercent() {
return this._showSidebar ? '20%' : StrCast(this.layoutDoc._sidebarWidthPercent, '0%');
}
@@ -244,6 +250,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
!this.layoutDoc.showSidebar && this.toggleSidebar();
this._sidebarRef.current?.anchorMenuClick(this.getAnchor());
};
+ AnchorMenu.Instance.OnAudio = (e: PointerEvent) => {
+ !this.layoutDoc.showSidebar && this.toggleSidebar();
+ const anchor = this.getAnchor();
+ const target = this._sidebarRef.current?.anchorMenuClick(anchor);
+ if (target) {
+ anchor.followLinkAudio = true;
+ DocumentViewInternal.recordAudioAnnotation(Doc.GetProto(target), Doc.LayoutFieldKey(target));
+ target.title = ComputedField.MakeFunction(`self["text-audioAnnotations-text"].lastElement()`);
+ }
+ };
AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => {
this._editorView?.state && RichTextMenu.Instance.setHighlight(color, this._editorView, this._editorView?.dispatch);
return undefined;
@@ -377,9 +393,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
};
autoLink = () => {
+ const newAutoLinks = new Set<Doc>();
+ const oldAutoLinks = DocListCast(this.props.Document.links).filter(link => link.linkRelationship === LinkManager.AutoKeywords);
if (this._editorView?.state.doc.textContent) {
- const newAutoLinks = new Set<Doc>();
- const oldAutoLinks = DocListCast(this.props.Document.links).filter(link => link.linkRelationship === LinkManager.AutoKeywords);
const f = this._editorView.state.selection.from;
const t = this._editorView.state.selection.to;
var tr = this._editorView.state.tr as any;
@@ -388,12 +404,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
DocListCast(Doc.MyPublishedDocs.data).forEach(term => (tr = this.hyperlinkTerm(tr, term, newAutoLinks)));
tr = tr.setSelection(new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
this._editorView?.dispatch(tr);
- oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.anchor2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
}
+ oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.anchor2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
};
updateTitle = () => {
- const title = StrCast(this.dataDoc.title);
+ const title = StrCast(this.dataDoc.title, Cast(this.dataDoc.title, RichTextField, null)?.Text);
if (
!this.props.dontRegisterView && // (this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing
(title.startsWith('-') || title.startsWith('@')) &&
@@ -421,28 +437,26 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const editorView = this._editorView;
if (editorView && (editorView as any).docView && !Doc.AreProtosEqual(target, this.rootDoc)) {
const autoLinkTerm = StrCast(target.title).replace(/^@/, '');
- const flattened1 = this.findInNode(editorView, editorView.state.doc, autoLinkTerm);
var alink: Doc | undefined;
- flattened1.forEach((flat, i) => {
- const flattened = this.findInNode(this._editorView!, this._editorView!.state.doc, autoLinkTerm);
- this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
+ this.findInNode(editorView, editorView.state.doc, autoLinkTerm).forEach(sel => {
const splitter = editorView.state.schema.marks.splitter.create({ id: Utils.GenerateGuid() });
- const sel = flattened[i];
- tr = tr.addMark(sel.from, sel.to, splitter);
- tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
- if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
- alink =
- alink ??
- (DocListCast(this.Document.links).find(link => Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) ||
- DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, LinkManager.AutoKeywords)!);
- newAutoLinks.add(alink);
- const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }];
- allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? []));
- const link = editorView.state.schema.marks.autoLinkAnchor.create({ allAnchors, title: 'auto term', location: 'add:right' });
- tr = tr.addMark(pos, pos + node.nodeSize, link);
- }
- });
- tr = tr.removeMark(sel.from, sel.to, splitter);
+ if (!sel.$anchor.pos || editorView.state.doc.textBetween(sel.$anchor.pos - 1, sel.$to.pos).trim() === autoLinkTerm) {
+ tr = tr.addMark(sel.from, sel.to, splitter);
+ tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
+ if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
+ alink =
+ alink ??
+ (DocListCast(this.Document.links).find(link => Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) ||
+ DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, LinkManager.AutoKeywords)!);
+ newAutoLinks.add(alink);
+ const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }];
+ allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? []));
+ const link = editorView.state.schema.marks.autoLinkAnchor.create({ allAnchors, title: 'auto term', location: 'add:right' });
+ tr = tr.addMark(pos, pos + node.nodeSize, link);
+ }
+ });
+ tr = tr.removeMark(sel.from, sel.to, splitter);
+ }
});
}
return tr;
@@ -633,18 +647,35 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
@action
toggleSidebar = (preview: boolean = false) => {
- const prevWidth = this.sidebarWidth();
+ const prevWidth = 1 - this.sidebarWidth() / Number(getComputedStyle(this._ref.current!).width.replace('px', ''));
if (preview) this._showSidebar = true;
else this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%';
- this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
+ this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) * prevWidth);
};
sidebarDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), false);
+ const batch = UndoManager.StartBatch('sidebar');
+ setupMoveUpEvents(
+ this,
+ e,
+ this.sidebarMove,
+ (e, movement, isClick) => !isClick && batch.end(),
+ () => {
+ this.toggleSidebar();
+ batch.end();
+ },
+ true
+ );
};
sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
- const bounds = this._ref.current!.getBoundingClientRect();
- this.layoutDoc._sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
+ const localDelta = this.props
+ .ScreenToLocalTransform()
+ .scale(this.props.NativeDimScaling?.() || 1)
+ .transformDirection(delta[0], delta[1]);
+ const sidebarWidth = (NumCast(this.layoutDoc._width) * Number(this.sidebarWidthPercent.replace('%', ''))) / 100;
+ const width = this.layoutDoc[WidthSym]() + localDelta[0];
+ this.layoutDoc._sidebarWidthPercent = Math.max(0, (sidebarWidth + localDelta[0]) / width) * 100 + '%';
+ this.layoutDoc.width = width;
this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== '0%';
e.preventDefault();
return false;
@@ -672,9 +703,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const editor = this._editorView!;
const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
- let target = (e.target as any).parentElement; // hrefs are stored on the database of the <a> node that wraps the hyerlink <span>
+ let target = e.target as any; // hrefs are stored on the database of the <a> node that wraps the hyerlink <span>
while (target && !target.dataset?.targethrefs) target = target.parentElement;
- if (target) {
+ if (target && !(e.nativeEvent as any).dash) {
const hrefs = (target.dataset?.targethrefs as string)
?.trim()
.split(' ')
@@ -693,6 +724,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
})
);
+ e.preventDefault();
e.stopPropagation();
return;
}
@@ -751,7 +783,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const uicontrols: ContextMenuProps[] = [];
!Doc.noviceMode && uicontrols.push({ description: `${FormattedTextBox._canAnnotate ? "Don't" : ''} Show Menu on Selections`, 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.noviceMode &&
uicontrols.push({
@@ -833,23 +864,28 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
setDictationContent = (value: string) => {
if (this._editorView && this._recordingStart) {
if (this._break) {
- const textanchor = Docs.Create.TextanchorDocument({ title: 'dictation anchor' });
- this.addDocument(textanchor);
- const link = DocUtils.MakeLinkToActiveAudio(() => textanchor, false).lastElement();
- link && (Doc.GetProto(link).isDictation = true);
- if (!link) return;
- const audioanchor = Cast(link.anchor2, Doc, null);
- if (!audioanchor) return;
- audioanchor.backgroundColor = 'tan';
- const audiotag = this._editorView.state.schema.nodes.audiotag.create({
- timeCode: NumCast(audioanchor._timecodeToShow),
- audioId: audioanchor[Id],
- textId: textanchor[Id],
- });
- Doc.GetProto(textanchor).title = 'dictation:' + audiotag.attrs.timeCode;
- const tr = this._editorView.state.tr.insert(this._editorView.state.doc.content.size, audiotag);
- const tr2 = tr.setSelection(TextSelection.create(tr.doc, tr.doc.content.size));
- this._editorView.dispatch(tr.setSelection(TextSelection.create(tr2.doc, tr2.doc.content.size)));
+ const textanchorFunc = () => {
+ const tanch = Docs.Create.TextanchorDocument({ title: 'dictation anchor' });
+ return this.addDocument(tanch) ? tanch : undefined;
+ };
+ const link = DocUtils.MakeLinkToActiveAudio(textanchorFunc, false).lastElement();
+ if (link) {
+ Doc.GetProto(link).isDictation = true;
+ const audioanchor = Cast(link.anchor2, Doc, null);
+ const textanchor = Cast(link.anchor1, Doc, null);
+ if (audioanchor) {
+ audioanchor.backgroundColor = 'tan';
+ const audiotag = this._editorView.state.schema.nodes.audiotag.create({
+ timeCode: NumCast(audioanchor._timecodeToShow),
+ audioId: audioanchor[Id],
+ textId: textanchor[Id],
+ });
+ Doc.GetProto(textanchor).title = 'dictation:' + audiotag.attrs.timeCode;
+ const tr = this._editorView.state.tr.insert(this._editorView.state.doc.content.size, audiotag);
+ const tr2 = tr.setSelection(TextSelection.create(tr.doc, tr.doc.content.size));
+ this._editorView.dispatch(tr.setSelection(TextSelection.create(tr2.doc, tr2.doc.content.size)));
+ }
+ }
}
const from = this._editorView.state.selection.from;
this._break = false;
@@ -957,7 +993,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
};
@computed get contentScaling() {
- return Doc.NativeAspect(this.rootDoc, this.dataDoc, false) ? this.props.scaling?.() || 1 : 1;
+ return Doc.NativeAspect(this.rootDoc, this.dataDoc, false) ? this.props.NativeDimScaling?.() || 1 : 1;
}
componentDidMount() {
!this.props.dontSelectOnLoad && 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.
@@ -978,7 +1014,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
// set the document height when one of the component heights changes and autoHeight is on
() => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, autoHeight: this.autoHeight, marginsHeight: this.autoHeightMargins }),
({ sidebarHeight, textHeight, autoHeight, marginsHeight }) => {
- autoHeight && this.props.setHeight?.(this.contentScaling * (marginsHeight + Math.max(sidebarHeight, textHeight)));
+ const newHeight = this.contentScaling * (marginsHeight + Math.max(sidebarHeight, textHeight));
+ if (autoHeight && newHeight && newHeight !== this.rootDoc.height) {
+ this.props.setHeight?.(newHeight);
+ }
},
{ fireImmediately: true }
);
@@ -1436,14 +1475,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
document.removeEventListener('pointermove', this.onSelectMove);
};
onPointerUp = (e: React.PointerEvent): void => {
- if (!this._editorView?.state.selection.empty && FormattedTextBox._canAnnotate) this.setupAnchorMenu();
+ if (!this._editorView?.state.selection.empty && FormattedTextBox._canAnnotate && !(e.nativeEvent as any).dash) this.setupAnchorMenu();
if (!this._downEvent) return;
this._downEvent = false;
- if (this.props.isContentActive(true)) {
+ if (this.props.isContentActive(true) && !(e.nativeEvent as any).dash) {
const editor = this._editorView!;
const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
!this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0))));
- let target = (e.target as any).parentElement; // hrefs are stored on the database of the <a> node that wraps the hyerlink <span>
+ let target = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span>
while (target && !target.dataset?.targethrefs) target = target.parentElement;
FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc);
}
@@ -1479,7 +1518,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
onFocused = (e: React.FocusEvent): void => {
//applyDevTools.applyDevTools(this._editorView);
FormattedTextBox.Focused = this;
- this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props);
+ this.ProseRef?.children[0] === e.nativeEvent.target && this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props);
};
@observable public static Focused: FormattedTextBox | undefined;
@@ -1567,6 +1606,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
@action
onBlur = (e: any) => {
+ if (this.ProseRef?.children[0] !== e.nativeEvent.target) return;
this.autoLink();
FormattedTextBox.Focused === this && (FormattedTextBox.Focused = undefined);
if (RichTextMenu.Instance?.view === this._editorView && !this.props.isSelected(true)) {
@@ -1671,7 +1711,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (this.props.setHeight && scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) {
// if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
const setScrollHeight = () => (this.rootDoc[this.fieldKey + '-scrollHeight'] = scrollHeight);
- if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) {
+ if (this.rootDoc === this.layoutDoc || this.layoutDoc.resolvedDataDoc) {
setScrollHeight();
} else {
setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived...
@@ -1680,8 +1720,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
};
fitContentsToBox = () => this.props.Document._fitContentsToBox;
- sidebarContentScaling = () => (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
- sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
+ sidebarContentScaling = () => (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
+ sidebarAddDocument = (doc: Doc | Doc[], sidebarKey: string = this.SidebarKey) => {
if (!this.layoutDoc._showSidebar) this.toggleSidebar();
// console.log("printting allSideBarDocs");
// console.log(this.allSidebarDocs);
@@ -1694,13 +1734,23 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
sidebarScreenToLocal = () =>
this.props
.ScreenToLocalTransform()
- .translate(-(this.props.PanelWidth() - this.sidebarWidth()) / (this.props.scaling?.() || 1), 0)
- .scale(1 / NumCast(this.layoutDoc._viewScale, 1));
+ .translate(-(this.props.PanelWidth() - this.sidebarWidth()) / (this.props.NativeDimScaling?.() || 1), 0)
+ .scale(1 / NumCast(this.layoutDoc._viewScale, 1) / (this.props.NativeDimScaling?.() || 1));
@computed get audioHandle() {
- return (
- <div className="formattedTextBox-dictation" onClick={action(e => (this._recording = !this._recording))}>
- <FontAwesomeIcon className="formattedTextBox-audioFont" style={{ color: this._recording ? 'red' : 'blue', transitionDelay: '0.6s', opacity: this._recording ? 1 : 0.25 }} icon={'microphone'} size="sm" />
+ return !this._recording ? null : (
+ <div
+ className="formattedTextBox-dictation"
+ onPointerDown={e =>
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ emptyFunction,
+ action(e => (this._recording = !this._recording))
+ )
+ }>
+ <FontAwesomeIcon className="formattedTextBox-audioFont" style={{ color: 'red' }} icon={'microphone'} size="sm" />
</div>
);
}
@@ -1715,7 +1765,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
className="formattedTextBox-sidebar-handle"
onPointerDown={this.sidebarDown}
style={{
- left: `max(0px, calc(100% - ${this.sidebarWidthPercent} - 17px))`,
backgroundColor: backgroundColor,
color: color,
opacity: annotated ? 1 : undefined,
@@ -1726,7 +1775,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
@computed get sidebarCollection() {
const renderComponent = (tag: string) => {
- const ComponentTag = tag === 'freeform' ? CollectionFreeFormView : tag === 'translation' ? FormattedTextBox : CollectionStackingView;
+ const ComponentTag = tag === CollectionViewType.Freeform ? CollectionFreeFormView : tag === CollectionViewType.Tree ? CollectionTreeView : tag === 'translation' ? FormattedTextBox : CollectionStackingView;
return ComponentTag === CollectionStackingView ? (
<SidebarAnnos
ref={this._sidebarRef}
@@ -1735,7 +1784,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
rootDoc={this.rootDoc}
layoutDoc={this.layoutDoc}
dataDoc={this.dataDoc}
- // usePanelWidth={true}
+ ScreenToLocalTransform={this.sidebarScreenToLocal}
nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
showSidebar={this.SidebarShown}
@@ -1746,30 +1795,33 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
removeDocument={this.removeDocument}
/>
) : (
- <ComponentTag
- {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
- NativeWidth={returnZero}
- NativeHeight={returnZero}
- PanelHeight={this.props.PanelHeight}
- PanelWidth={this.sidebarWidth}
- xPadding={0}
- yPadding={0}
- scaleField={this.SidebarKey + '-scale'}
- isAnnotationOverlay={false}
- select={emptyFunction}
- scaling={this.sidebarContentScaling}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- removeDocument={this.sidebarRemDocument}
- moveDocument={this.sidebarMoveDocument}
- addDocument={this.sidebarAddDocument}
- CollectionView={undefined}
- ScreenToLocalTransform={this.sidebarScreenToLocal}
- renderDepth={this.props.renderDepth + 1}
- setHeight={this.setSidebarHeight}
- fitContentsToBox={this.fitContentsToBox}
- noSidebar={true}
- fieldKey={this.layoutDoc.sidebarViewType === 'translation' ? `${this.fieldKey}-translation` : `${this.fieldKey}-annotations`}
- />
+ <div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.props.DocumentView?.()!, false), true)}>
+ <ComponentTag
+ {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+ NativeWidth={returnZero}
+ NativeHeight={returnZero}
+ PanelHeight={this.props.PanelHeight}
+ PanelWidth={this.sidebarWidth}
+ xPadding={0}
+ yPadding={0}
+ scaleField={this.SidebarKey + '-scale'}
+ isAnnotationOverlay={false}
+ select={emptyFunction}
+ NativeDimScaling={this.sidebarContentScaling}
+ whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+ removeDocument={this.sidebarRemDocument}
+ moveDocument={this.sidebarMoveDocument}
+ addDocument={this.sidebarAddDocument}
+ CollectionView={undefined}
+ ScreenToLocalTransform={this.sidebarScreenToLocal}
+ renderDepth={this.props.renderDepth + 1}
+ setHeight={this.setSidebarHeight}
+ fitContentsToBox={this.fitContentsToBox}
+ noSidebar={true}
+ treeViewHideTitle={true}
+ fieldKey={this.layoutDoc.sidebarViewType === 'translation' ? `${this.fieldKey}-translation` : `${this.fieldKey}-sidebar`}
+ />
+ </div>
);
};
return (
@@ -1782,7 +1834,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
TraceMobx();
const active = this.props.isContentActive() || this.props.isSelected();
const selected = active;
- const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
+ const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
const rounded = StrCast(this.layoutDoc.borderRounding) === '100%' ? '-rounded' : '';
const interactive = (Doc.ActiveTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
@@ -1832,7 +1884,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
style={{
width: this.props.dontSelectOnLoad ? '100%' : `calc(100% - ${this.sidebarWidthPercent})`,
pointerEvents: !active && !SnappingManager.GetIsDragging() ? 'none' : undefined,
- overflow: this.layoutDoc._singleLine ? 'hidden' : undefined,
+ overflow: this.layoutDoc._singleLine ? 'hidden' : this.layoutDoc._autoHeight ? 'visible' : undefined,
}}
onScroll={this.onScroll}
onDrop={this.ondrop}>
@@ -1849,9 +1901,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}}
/>
</div>
- {this.props.noSidebar || this.Document._noSidebar || this.props.dontSelectOnLoad || !this.SidebarShown || this.sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
- {this.props.noSidebar || this.Document._noSidebar || this.props.dontSelectOnLoad || this.Document._singleLine ? null : this.sidebarHandle}
- {!this.layoutDoc._showAudio ? null : this.audioHandle}
+ {this.noSidebar || this.props.dontSelectOnLoad || !this.SidebarShown || this.sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
+ {this.noSidebar || this.Document._noSidebar || this.props.dontSelectOnLoad || this.Document._singleLine ? null : this.sidebarHandle}
+ {this.audioHandle}
</div>
</div>
);
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 22ca76b2e..2a77210ae 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -60,7 +60,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@observable private showLinkDropdown: boolean = false;
_reaction: IReactionDisposer | undefined;
- _delayHide = false;
constructor(props: Readonly<{}>) {
super(props);
runInAction(() => {
@@ -70,16 +69,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
});
}
- componentDidMount() {
- this._reaction = reaction(
- () => SelectionManager.Views(),
- () => this._delayHide && !(this._delayHide = false) && this.fadeOut(true)
- );
- }
- componentWillUnmount() {
- this._reaction?.();
- }
-
@computed get noAutoLink() {
return this._noLinkActive;
}
@@ -108,8 +97,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
return this._activeAlignment;
}
- public delayHide = () => (this._delayHide = true);
-
@action
public updateMenu(view: EditorView | undefined, lastState: EditorState | undefined, props: any) {
if (this._linkToRef.current?.getBoundingClientRect().width) {
@@ -394,7 +381,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
// remove all node type and apply the passed-in one to the selected text
changeListType = (mapStyle: string) => {
const active = this.view?.state && RichTextMenu.Instance.getActiveListStyle();
- const nodeType = this.view?.state.schema.nodes.ordered_list.create({ mapStyle: active === mapStyle ? "" : mapStyle });
+ const nodeType = this.view?.state.schema.nodes.ordered_list.create({ mapStyle: active === mapStyle ? '' : mapStyle });
if (!this.view || nodeType?.attrs.mapStyle === '') return;
const nextIsOL = this.view.state.selection.$from.nodeAfter?.type === schema.nodes.ordered_list;
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 292c187e4..3bbdce1e4 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -29,10 +29,11 @@ import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentVie
import { FieldView, FieldViewProps } from '../FieldView';
import './PresBox.scss';
import { PresEffect, PresMovement, PresStatus } from './PresEnums';
+import { CollectionFreeFormViewChrome } from '../../collections/CollectionMenu';
export interface PinProps {
audioRange?: boolean;
- setPosition?: boolean;
+ activeFrame?: number;
hidePresBox?: boolean;
pinWithView?: PinViewProps;
pinDocView?: boolean; // whether the current view specs of the document should be saved the pinned document
@@ -399,7 +400,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
const collectionDocView = presCollection ? DocumentManager.Instance.getDocumentView(presCollection) : undefined;
const includesDoc: boolean = DocListCast(presCollection?.data).includes(targetDoc);
- const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === srcContext);
+ const tab = CollectionDockingView.Instance && Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === srcContext);
this.turnOffEdit();
// Handles the setting of presCollection
if (includesDoc) {
@@ -509,7 +510,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
this.childDocs.forEach((doc, index) => {
const curDoc = Cast(doc, Doc, null);
const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
- if (tagDoc) tagDoc.opacity = 1;
+ //if (tagDoc) tagDoc.opacity = 1;
const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc);
const curInd: number = itemIndexes.indexOf(index);
if (tagDoc === this.layoutDoc.presCollection) {
@@ -519,7 +520,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
} else if (curDoc.presHideBefore) {
if (index > this.itemIndex) {
tagDoc.opacity = 0;
- } else if (!curDoc.presHideAfter) {
+ } else if (index === this.itemIndex || !curDoc.presHideAfter) {
tagDoc.opacity = 1;
}
}
@@ -527,7 +528,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
} else if (curDoc.presHideAfter) {
if (index < this.itemIndex) {
tagDoc.opacity = 0;
- } else if (!curDoc.presHideBefore) {
+ } else if (index === this.itemIndex || !curDoc.presHideBefore) {
tagDoc.opacity = 1;
}
}
@@ -821,17 +822,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(context), 0);
else this.updateCurrentPresentation(context);
- if (this.activeItem.setPosition && this.activeItem.y !== undefined && this.activeItem.x !== undefined && this.targetDoc.x !== undefined && this.targetDoc.y !== undefined) {
- const timer = (ms: number) => new Promise(res => (this._presTimer = setTimeout(res, ms)));
- const time = 10;
- const ydiff = NumCast(this.activeItem.y) - NumCast(this.targetDoc.y);
- const xdiff = NumCast(this.activeItem.x) - NumCast(this.targetDoc.x);
-
- for (let i = 0; i < time; i++) {
- this.targetDoc.x = NumCast(this.targetDoc.x) + xdiff / time;
- this.targetDoc.y = NumCast(this.targetDoc.y) + ydiff / time;
- await timer(0.1);
- }
+ if (this.activeItem.presActiveFrame !== undefined) {
+ const context = DocCast(DocCast(this.activeItem.presentationTargetDoc).context);
+ context && CollectionFreeFormViewChrome.gotoKeyFrame(context, NumCast(this.activeItem.presActiveFrame));
}
};
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 1a2054676..0cf15d297 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -199,11 +199,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
};
- headerUp = (e: React.PointerEvent<HTMLDivElement>) => {
- e.stopPropagation();
- e.preventDefault();
- };
-
/**
* Function to drag and drop the pres element to a diferent location
*/
@@ -219,8 +214,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
const dragItem: HTMLElement[] = [];
if (dragArray.length === 1) {
const doc = this._itemRef.current || dragArray[0];
- doc.className = miniView ? 'presItem-miniSlide' : 'presItem-slide';
- dragItem.push(doc);
+ if (doc) {
+ doc.className = miniView ? 'presItem-miniSlide' : 'presItem-slide';
+ dragItem.push(doc);
+ }
} else if (dragArray.length >= 1) {
const doc = document.createElement('div');
doc.className = 'presItem-multiDrag';
@@ -475,8 +472,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
})}
onPointerOver={this.onPointerOver}
onPointerLeave={this.onPointerLeave}
- onPointerDown={this.headerDown}
- onPointerUp={this.headerUp}>
+ onPointerDown={this.headerDown}>
{/* {miniView ?
// when width is LESS than 110 px
<div className={`presItem-miniSlide ${isSelected ? "active" : ""}`} ref={miniView ? this._dragRef : null}>