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.tsx254
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx22
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx384
-rw-r--r--src/client/views/nodes/DocumentView.scss34
-rw-r--r--src/client/views/nodes/DocumentView.tsx193
-rw-r--r--src/client/views/nodes/EquationBox.tsx100
-rw-r--r--src/client/views/nodes/FieldView.tsx65
-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/KeyValueBox.tsx272
-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/LoadingBox.scss35
-rw-r--r--src/client/views/nodes/LoadingBox.tsx47
-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.scss20
-rw-r--r--src/client/views/nodes/VideoBox.tsx304
-rw-r--r--src/client/views/nodes/WebBox.scss17
-rw-r--r--src/client/views/nodes/WebBox.tsx220
-rw-r--r--src/client/views/nodes/button/FontIconBox.scss34
-rw-r--r--src/client/views/nodes/button/FontIconBox.tsx204
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx30
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx16
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss553
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx291
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx123
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts12
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx1731
-rw-r--r--src/client/views/nodes/trails/PresElementBox.tsx208
-rw-r--r--src/client/views/nodes/trails/PresEnums.ts42
37 files changed, 2962 insertions, 2788 deletions
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 284584a3d..a48906372 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,26 +1,27 @@
-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 } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, Opt } from '../../../fields/Doc';
+import { InkField } from '../../../fields/InkField';
+import { List } from '../../../fields/List';
+import { listSpec } from '../../../fields/Schema';
+import { ComputedField } from '../../../fields/ScriptField';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
+import { numberRange } from '../../../Utils';
+import { DocumentType } from '../../documents/DocumentTypes';
+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 { 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; color?: string; backgroundColor?: string; 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,65 +33,131 @@ 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: { key: string; val?: number }[] = [
+ { key: '_height' },
+ { key: '_width' },
+ { key: 'x' },
+ { key: 'y' },
+ { key: '_jitterRotation', val: 0 },
+ { key: '_scrollTop' },
+ { key: 'opacity', val: 1 },
+ { key: 'viewScale', val: 1 },
+ { key: 'panX' },
+ { key: 'panY' },
+ ]; // fields that are configured to be animatable using animation frames
+ public static animStringFields = ['backgroundColor', 'color']; // fields that are configured to be animatable using animation frames
+ public static animDataFields = (doc: Doc) => (Doc.LayoutFieldKey(doc) ? [Doc.LayoutFieldKey(doc)] : []); // 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() {
+ // this makes mobx trace() statements more descriptive
+ return 'CollectionFreeFormDocumentView(' + this.rootDoc.title + ')';
+ }
+
+ get transform() {
+ return `translate(${this.X}px, ${this.Y}px) rotate(${NumCast(this.Document.jitterRotation, this.props.jitterRotation)}deg)`;
+ }
+ get X() {
+ return this.dataProvider?.x ?? NumCast(this.Document.x);
+ }
+ get Y() {
+ return this.dataProvider?.y ?? NumCast(this.Document.y);
+ }
+ get ZInd() {
+ return this.dataProvider?.zIndex ?? NumCast(this.Document.zIndex);
+ }
+ get Opacity() {
+ return this.dataProvider?.opacity;
+ }
+ get BackgroundColor() {
+ return this.dataProvider?.backgroundColor ?? Cast(this.Document._backgroundColor, 'string', null);
+ }
+ get Color() {
+ return this.dataProvider?.color ?? Cast(this.Document._color, 'string', null);
+ }
+ get Highlight() {
+ return this.dataProvider?.highlight;
+ }
+ @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
+ if (doc === this.layoutDoc)
+ // prettier-ignore
+ switch (property) {
+ case StyleProp.Opacity: return this.Opacity; // only change the opacity for this specific document, not its children
+ case StyleProp.BackgroundColor: return this.BackgroundColor;
+ case StyleProp.Color: return this.Color;
+ }
return this.props.styleProvider?.(doc, props, property);
- }
+ };
- public static getValues(doc: Doc, time: number) {
+ public static getValues(doc: Doc, time: number, fillIn: boolean = true) {
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.key] = Cast(doc[`${val.key}-indexed`], listSpec('number'), fillIn ? [NumCast(doc[val.key], val.val)] : []).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as number);
return p;
}, {} as { [val: string]: Opt<number> });
}
+ public static getStringValues(doc: Doc, time: number) {
+ return CollectionFreeFormDocumentView.animStringFields.reduce((p, val) => {
+ p[val] = Cast(doc[`${val}-indexed`], listSpec('string'), [StrCast(doc[val])]).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as string);
+ return p;
+ }, {} as { [val: string]: Opt<string> });
+ }
+
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.key}-indexed`], listSpec('number'), null);
+ findexed?.length <= timecode + 1 && findexed.push(undefined as any as number);
+ });
+ CollectionFreeFormDocumentView.animStringFields.forEach(val => {
+ const findexed = Cast(doc[`${val}-indexed`], listSpec('string'), null);
+ findexed?.length <= timecode + 1 && findexed.push(undefined as any as string);
+ });
+ CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => {
+ const findexed = Cast(doc[`${val}-indexed`], listSpec(InkField), null);
+ findexed?.length <= timecode + 1 && findexed.push(undefined as any);
+ });
+ })
+ );
+ 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);
+ public static gotoKeyframe(docs: Doc[], duration = 1000) {
+ docs.forEach(doc => (doc._viewTransition = doc.dataTransition = `all ${duration}ms`));
+ setTimeout(
+ () =>
+ docs.forEach(doc => {
+ doc._viewTransition = undefined;
+ doc.dataTransition = 'inherit';
+ }),
+ 1010
+ );
}
public static setupZoom(doc: Doc, targDoc: Doc) {
@@ -102,21 +169,26 @@ 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.key] = ComputedField.MakeInterpolatedNumber(val.key, 'activeFrame', doc, currTimecode, val.val)));
+ CollectionFreeFormDocumentView.animStringFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedString(val, 'activeFrame', doc, currTimecode)));
+ CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => (Doc.GetProto(doc)[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', Doc.GetProto(doc), currTimecode)));
+ const targetDoc = doc.type === DocumentType.RTF ? Doc.GetProto(doc) : doc; // data fields, like rtf 'text' exist on the data doc, so
+ doc !== targetDoc && (targetDoc.context = doc.context); // the computed fields don't see the layout doc -- need to copy the context to the data doc (HACK!!!) and set the activeFrame on the data doc (HACK!!!)
+ targetDoc.activeFrame = ComputedField.MakeFunction('self.context?._currentFrame||0');
+ targetDoc.dataTransition = 'inherit';
});
}
@@ -131,7 +203,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 +213,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;
@@ -162,25 +234,25 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
PanelWidth: this.panelWidth,
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>;
+ 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,
+ 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..6d6609317 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -25,7 +25,7 @@ import './DocumentView.scss';
import { EquationBox } from './EquationBox';
import { FieldView, FieldViewProps } from './FieldView';
import { FilterBox } from './FilterBox';
-import { FormattedTextBox, FormattedTextBoxProps } from './formattedText/FormattedTextBox';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
import { FunctionPlotBox } from './FunctionPlotBox';
import { ImageBox } from './ImageBox';
import { KeyValueBox } from './KeyValueBox';
@@ -43,6 +43,7 @@ import { VideoBox } from './VideoBox';
import { WebBox } from './WebBox';
import React = require('react');
import XRegExp = require('xregexp');
+import { LoadingBox } from './LoadingBox';
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
@@ -114,14 +115,13 @@ export class HTMLtag extends React.Component<HTMLtagProps> {
@observer
export class DocumentContentsView extends React.Component<
- DocumentViewProps &
- FormattedTextBoxProps & {
- isSelected: (outsideReaction: boolean) => boolean;
- select: (ctrl: boolean) => void;
- scaling?: () => number;
- setHeight?: (height: number) => void;
- layoutKey: string;
- }
+ DocumentViewProps & {
+ isSelected: (outsideReaction: boolean) => boolean;
+ select: (ctrl: boolean) => void;
+ NativeDimScaling?: () => number;
+ setHeight?: (height: number) => void;
+ layoutKey: string;
+ }
> {
@computed get layout(): string {
TraceMobx();
@@ -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);
@@ -268,6 +267,7 @@ export class DocumentContentsView extends React.Component<
DataVizBox,
HTMLtag,
ComparisonBox,
+ LoadingBox,
}}
bindings={bindings}
jsx={layoutFrame}
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..6cadeec41 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -1,7 +1,8 @@
-@import "../global/globalCssVariables";
+@import '../global/globalCssVariables';
.documentView-effectsWrapper {
border-radius: inherit;
+ transition: inherit;
}
// documentViews have a docView-hack tag which is replaced by this tag when capturing bitmaps (when the dom is converted to an html string)
@@ -24,8 +25,7 @@
width: 100%;
height: 100%;
border-radius: inherit;
- transition: outline .3s linear;
- cursor: grab;
+ transition: outline 0.3s linear;
// background: $white; //overflow: hidden;
transform-origin: left top;
@@ -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;
}
}
@@ -97,6 +99,7 @@
border-radius: inherit;
width: 100%;
height: 100%;
+ transition: inherit;
.sharingIndicator {
height: 30px;
@@ -158,7 +161,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 +190,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;
}
}
@@ -210,10 +212,12 @@
display: flex;
width: 100%;
height: 100%;
+ transition: inherit;
.contentFittingDocumentView-previewDoc {
position: relative;
display: inline;
+ transition: inherit;
}
.contentFittingDocumentView-input {
@@ -225,6 +229,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..e628c2e44 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,8 +1,9 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
-import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
+import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
import { Document } from '../../../fields/documentSchemas';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
@@ -20,6 +21,7 @@ import { DocServer } from '../../DocServer';
import { Docs, DocUtils } from '../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
import { Networking } from '../../Network';
+import { DictationManager } from '../../util/DictationManager';
import { DocumentManager } from '../../util/DocumentManager';
import { DragManager, dropActionType } from '../../util/DragManager';
import { InteractionUtils } from '../../util/InteractionUtils';
@@ -37,7 +39,6 @@ import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { DocComponent } from '../DocComponent';
import { EditableView } from '../EditableView';
-import { InkingStroke } from '../InkingStroke';
import { LightboxView } from '../LightboxView';
import { StyleProp } from '../StyleProvider';
import { CollectionFreeFormDocumentView } from './CollectionFreeFormDocumentView';
@@ -156,6 +157,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 +177,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 +205,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 +236,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 +429,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,20 +492,27 @@ 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]);
+ // dragData.offset[0] = Math.min(this.rootDoc[WidthSym](), dragData.offset[0]); // bcz: this was breaking dragging rotated objects since the offset may be out of bounds with regard to the unrotated document
+ // dragData.offset[1] = Math.min(this.rootDoc[HeightSym](), dragData.offset[1]);
dragData.dropAction = dropAction;
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 +724,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 +758,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 +877,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 : [];
@@ -956,12 +969,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
collectionFilters = () => StrListCast(this.props.Document._docFilters);
collectionRangeDocFilters = () => StrListCast(this.props.Document._docRangeFilters);
@computed get showFilterIcon() {
- return this.collectionFilters().length || this.collectionRangeDocFilters().length ? 'hasFilter' : this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? 'inheritsFilter' : undefined;
+ return this.collectionFilters().length || this.collectionRangeDocFilters().length
+ ? 'hasFilter'
+ : this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f) && f !== Utils.noDragsDocFilter).length || this.props.docRangeFilters().length
+ ? 'inheritsFilter'
+ : undefined;
}
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 +1005,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 +1063,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 +1077,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 +1156,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 +1265,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 +1320,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,11 +1331,21 @@ 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,
+ cursor: Doc.ActiveTool === InkTool.None ? 'grab' : 'crosshair',
color: StrCast(this.layoutDoc.color, 'inherit'),
fontFamily: StrCast(this.Document._fontFamily, 'inherit'),
fontSize: Cast(this.Document._fontSize, 'string', null),
@@ -1492,7 +1540,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 +1551,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 +1585,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 +1616,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 +1644,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 = () => (Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined);
+ const yshift = () => (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 (
@@ -1602,7 +1656,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
ref={this.ContentRef}
style={{
transition: this.props.dataTransition,
- position: this.props.Document.isInkMask ? 'absolute' : undefined,
transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`,
width: isButton || isPresTreeElement ? '100%' : xshift() ?? `${(100 * (this.props.PanelWidth() - this.Xshift * 2)) / this.props.PanelWidth()}%`,
height:
@@ -1618,9 +1671,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 +1696,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..77aaa4441 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,26 +22,31 @@ 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;
+ pointerEvents?: () => Opt<string>;
// properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
- pointerEvents?: () => Opt<string>;
+ // See currentUserUtils headerTemplate for examples of creating text boxes from html which set some of these fields
+ // Also, see InkingStroke for examples of creating text boxes from render() methods which set some of these fields
+ backgroundColor?: string;
+ color?: string;
fontSize?: number;
height?: number;
width?: number;
- background?: string;
- color?: string;
xPadding?: number;
yPadding?: number;
+ noSidebar?: boolean;
+ dontScale?: boolean;
+ dontSelectOnLoad?: boolean; // suppress selecting (e.g.,. text box) when loaded (and mark as not being associated with scrollTop document field)
}
@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 +79,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/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 4b1fbaf7d..d9f46509e 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -1,34 +1,37 @@
-
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, Field, FieldResult } from "../../../fields/Doc";
-import { List } from "../../../fields/List";
-import { RichTextField } from "../../../fields/RichTextField";
-import { listSpec } from "../../../fields/Schema";
-import { ComputedField, ScriptField } from "../../../fields/ScriptField";
-import { Cast, FieldValue, NumCast } from "../../../fields/Types";
-import { ImageField } from "../../../fields/URLField";
-import { Docs } from "../../documents/Documents";
-import { SetupDrag } from "../../util/DragManager";
-import { CompiledScript, CompileScript, ScriptOptions } from "../../util/Scripting";
-import { undoBatch } from "../../util/UndoManager";
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, Field, FieldResult } from '../../../fields/Doc';
+import { List } from '../../../fields/List';
+import { RichTextField } from '../../../fields/RichTextField';
+import { listSpec } from '../../../fields/Schema';
+import { ComputedField, ScriptField } from '../../../fields/ScriptField';
+import { Cast, DocCast, FieldValue, NumCast } from '../../../fields/Types';
+import { ImageField } from '../../../fields/URLField';
+import { Docs } from '../../documents/Documents';
+import { SetupDrag } from '../../util/DragManager';
+import { CompiledScript, CompileScript, ScriptOptions } from '../../util/Scripting';
+import { undoBatch } from '../../util/UndoManager';
import { FieldView, FieldViewProps } from './FieldView';
-import "./KeyValueBox.scss";
-import { KeyValuePair } from "./KeyValuePair";
-import React = require("react");
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import e = require("express");
+import './KeyValueBox.scss';
+import { KeyValuePair } from './KeyValuePair';
+import React = require('react');
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
+import e = require('express');
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
+import { ImageBox } from './ImageBox';
export type KVPScript = {
script: CompiledScript;
- type: "computed" | "script" | false;
+ type: 'computed' | 'script' | false;
onDelegate: boolean;
};
@observer
export class KeyValueBox extends React.Component<FieldViewProps> {
- public static LayoutString(fieldStr: string) { return FieldView.LayoutString(KeyValueBox, fieldStr); }
+ public static LayoutString(fieldStr: string) {
+ return FieldView.LayoutString(KeyValueBox, fieldStr);
+ }
private _mainCont = React.createRef<HTMLDivElement>();
private _keyHeader = React.createRef<HTMLTableHeaderCellElement>();
@@ -37,8 +40,12 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
@observable private rows: KeyValuePair[] = [];
- @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); }
- get fieldDocToLayout() { return this.props.fieldKey ? Cast(this.props.Document[this.props.fieldKey], Doc, null) : this.props.Document; }
+ @computed get splitPercentage() {
+ return NumCast(this.props.Document.schemaSplitPercentage, 50);
+ }
+ get fieldDocToLayout() {
+ return this.props.fieldKey ? Cast(this.props.Document[this.props.fieldKey], Doc, null) : this.props.Document;
+ }
@action
onEnterKey = (e: React.KeyboardEvent): void => {
@@ -46,19 +53,19 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
e.stopPropagation();
if (this._keyInput.current?.value && this._valInput.current?.value && this.fieldDocToLayout) {
if (KeyValueBox.SetField(this.fieldDocToLayout, this._keyInput.current.value, this._valInput.current.value)) {
- this._keyInput.current.value = "";
- this._valInput.current.value = "";
+ this._keyInput.current.value = '';
+ this._valInput.current.value = '';
document.body.focus();
}
}
}
- }
+ };
public static CompileKVPScript(value: string): KVPScript | undefined {
- const eq = value.startsWith("=");
+ const eq = value.startsWith('=');
value = eq ? value.substr(1) : value;
- const dubEq = value.startsWith(":=") ? "computed" : value.startsWith(";=") ? "script" : false;
+ const dubEq = value.startsWith(':=') ? 'computed' : value.startsWith(';=') ? 'script' : false;
value = dubEq ? value.substr(2) : value;
- const options: ScriptOptions = { addReturn: true, params: { this: Doc.name, self: Doc.name, _last_: "any", _readOnly_: "boolean" }, editable: false };
+ const options: ScriptOptions = { addReturn: true, params: { this: Doc.name, self: Doc.name, _last_: 'any', _readOnly_: 'boolean' }, editable: false };
if (dubEq) options.typecheck = false;
const script = CompileScript(value, options);
return !script.compiled ? undefined : { script, type: dubEq, onDelegate: eq };
@@ -67,11 +74,11 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
public static ApplyKVPScript(doc: Doc, key: string, kvpScript: KVPScript, forceOnDelegate?: boolean): boolean {
const { script, type, onDelegate } = kvpScript;
//const target = onDelegate ? Doc.Layout(doc.layout) : Doc.GetProto(doc); // bcz: TODO need to be able to set fields on layout templates
- const target = forceOnDelegate || onDelegate || key.startsWith("_") ? doc : doc.proto || doc;
+ const target = forceOnDelegate || onDelegate || key.startsWith('_') ? doc : doc.proto || doc;
let field: Field;
- if (type === "computed") {
+ if (type === 'computed') {
field = new ComputedField(script);
- } else if (type === "script") {
+ } else if (type === 'script') {
field = new ScriptField(script);
} else {
const res = script.run({ this: target }, console.log);
@@ -96,7 +103,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
if (e.buttons === 1 && this.props.isSelected(true)) {
e.stopPropagation();
}
- }
+ };
onPointerWheel = (e: React.WheelEvent): void => e.stopPropagation();
rowHeight = () => 30;
@@ -104,7 +111,11 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
@computed get createTable() {
const doc = this.fieldDocToLayout;
if (!doc) {
- return <tr><td>Loading...</td></tr>;
+ return (
+ <tr>
+ <td>Loading...</td>
+ </tr>
+ );
}
const realDoc = doc;
@@ -122,83 +133,102 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
let i = 0;
const self = this;
for (const key of Object.keys(ids).slice().sort()) {
- rows.push(<KeyValuePair doc={realDoc} addDocTab={this.props.addDocTab} PanelWidth={this.props.PanelWidth} PanelHeight={this.rowHeight}
- ref={(function () {
- let oldEl: KeyValuePair | undefined;
- return (el: KeyValuePair) => {
- if (oldEl) self.rows.splice(self.rows.indexOf(oldEl), 1);
- oldEl = el;
- if (el) self.rows.push(el);
- };
- })()} keyWidth={100 - this.splitPercentage} rowStyle={"keyValueBox-" + (i++ % 2 ? "oddRow" : "evenRow")} key={key} keyName={key} />);
+ rows.push(
+ <KeyValuePair
+ doc={realDoc}
+ addDocTab={this.props.addDocTab}
+ PanelWidth={this.props.PanelWidth}
+ PanelHeight={this.rowHeight}
+ ref={(function () {
+ let oldEl: KeyValuePair | undefined;
+ return (el: KeyValuePair) => {
+ if (oldEl) self.rows.splice(self.rows.indexOf(oldEl), 1);
+ oldEl = el;
+ if (el) self.rows.push(el);
+ };
+ })()}
+ keyWidth={100 - this.splitPercentage}
+ rowStyle={'keyValueBox-' + (i++ % 2 ? 'oddRow' : 'evenRow')}
+ key={key}
+ keyName={key}
+ />
+ );
}
return rows;
}
@computed get newKeyValue() {
- return <tr className="keyValueBox-valueRow">
- <td className="keyValueBox-td-key" onClick={(e) => { this._keyInput.current!.select(); e.stopPropagation(); }} style={{ width: `${100 - this.splitPercentage}%` }}>
- <input style={{ width: "100%" }} ref={this._keyInput} type="text" placeholder="Key" />
- </td>
- <td className="keyValueBox-td-value" onClick={(e) => { this._valInput.current!.select(); e.stopPropagation(); }} style={{ width: `${this.splitPercentage}%` }}>
- <input style={{ width: "100%" }} ref={this._valInput} type="text" placeholder="Value" onKeyDown={this.onEnterKey} />
- </td>
- </tr>;
+ return (
+ <tr className="keyValueBox-valueRow">
+ <td
+ className="keyValueBox-td-key"
+ onClick={e => {
+ this._keyInput.current!.select();
+ e.stopPropagation();
+ }}
+ style={{ width: `${100 - this.splitPercentage}%` }}>
+ <input style={{ width: '100%' }} ref={this._keyInput} type="text" placeholder="Key" />
+ </td>
+ <td
+ className="keyValueBox-td-value"
+ onClick={e => {
+ this._valInput.current!.select();
+ e.stopPropagation();
+ }}
+ style={{ width: `${this.splitPercentage}%` }}>
+ <input style={{ width: '100%' }} ref={this._valInput} type="text" placeholder="Value" onKeyDown={this.onEnterKey} />
+ </td>
+ </tr>
+ );
}
@action
onDividerMove = (e: PointerEvent): void => {
const nativeWidth = this._mainCont.current!.getBoundingClientRect();
- this.props.Document.schemaSplitPercentage = Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100));
- }
+ this.props.Document.schemaSplitPercentage = Math.max(0, 100 - Math.round(((e.clientX - nativeWidth.left) / nativeWidth.width) * 100));
+ };
@action
onDividerUp = (e: PointerEvent): void => {
- document.removeEventListener("pointermove", this.onDividerMove);
+ document.removeEventListener('pointermove', this.onDividerMove);
document.removeEventListener('pointerup', this.onDividerUp);
- }
+ };
onDividerDown = (e: React.PointerEvent) => {
e.stopPropagation();
e.preventDefault();
- document.addEventListener("pointermove", this.onDividerMove);
+ document.addEventListener('pointermove', this.onDividerMove);
document.addEventListener('pointerup', this.onDividerUp);
- }
+ };
- getTemplate = async () => {
- const parent = Docs.Create.StackingDocument([], { _width: 800, _height: 800, title: "Template", _chromeHidden: true });
- parent._columnWidth = 100;
- for (const row of this.rows.filter(row => row.isChecked)) {
- await this.createTemplateField(parent, row);
- row.uncheck();
+ getFieldView = async () => {
+ const rows = this.rows.filter(row => row.isChecked);
+ if (rows.length > 1) {
+ const parent = Docs.Create.StackingDocument([], { _autoHeight: true, _width: 300, title: `field views for ${DocCast(this.props.Document.data).title}`, _chromeHidden: true });
+ for (const row of rows) {
+ const field = this.createFieldView(DocCast(this.props.Document.data), row);
+ field && Doc.AddDocToList(parent, 'data', field);
+ row.uncheck();
+ }
+ return parent;
}
- return parent;
- }
+ return this.createFieldView(DocCast(this.props.Document.data), rows.lastElement());
+ };
- createTemplateField = async (parentStackingDoc: Doc, row: KeyValuePair) => {
+ createFieldView = (templateDoc: Doc, row: KeyValuePair) => {
const metaKey = row.props.keyName;
- const sourceDoc = await Cast(this.props.Document.data, Doc);
- if (!sourceDoc) {
- return;
- }
+ const fieldTemplate = Doc.MakeAlias(templateDoc);
+ fieldTemplate.title = metaKey;
+ fieldTemplate.layout = this.inferType(templateDoc[metaKey], metaKey);
+ return fieldTemplate;
+ };
- const fieldTemplate = await this.inferType(sourceDoc[metaKey], metaKey);
- if (!fieldTemplate) {
- return;
- }
- const previousViewType = fieldTemplate._viewType;
- Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(parentStackingDoc));
- previousViewType && (fieldTemplate._viewType = previousViewType);
-
- Cast(parentStackingDoc.data, listSpec(Doc))!.push(fieldTemplate);
- }
-
- inferType = async (data: FieldResult, metaKey: string) => {
+ inferType = (data: FieldResult, metaKey: string) => {
const options = { _width: 300, _height: 300, title: metaKey };
- if (data instanceof RichTextField || typeof data === "string" || typeof data === "number") {
- return Docs.Create.TextDocument("", options);
+ if (data instanceof RichTextField || typeof data === 'string' || typeof data === 'number') {
+ return FormattedTextBox.LayoutString(metaKey);
} else if (data instanceof List) {
if (data.length === 0) {
return Docs.Create.StackingDocument([], options);
}
- const first = await Cast(data[0], Doc);
+ const first = DocCast(data[0]);
if (!first || !first.data) {
return Docs.Create.StackingDocument([], options);
}
@@ -212,44 +242,52 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
return undefined;
}
} else if (data instanceof ImageField) {
- return Docs.Create.ImageDocument("https://image.flaticon.com/icons/png/512/23/23765.png", options);
+ return ImageBox.LayoutString(metaKey);
}
- return new Doc;
- }
+ return new Doc();
+ };
specificContextMenu = (e: React.MouseEvent): void => {
const cm = ContextMenu.Instance;
- const open = cm.findByDescription("Change Perspective...");
- const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : [];
+ const open = cm.findByDescription('Change Perspective...');
+ const openItems: ContextMenuProps[] = open && 'subitems' in open ? open.subitems : [];
openItems.push({
- description: "Default Perspective", event: () => {
- this.props.addDocTab(this.props.Document, "close");
- this.props.addDocTab(this.fieldDocToLayout, "add:right");
- }, icon: "image"
+ description: 'Default Perspective',
+ event: () => {
+ this.props.addDocTab(this.props.Document, 'close');
+ this.props.addDocTab(this.fieldDocToLayout, 'add:right');
+ },
+ icon: 'image',
});
- !open && cm.addItem({ description: "Change Perspective...", subitems: openItems, icon: "external-link-alt" });
- }
+ !open && cm.addItem({ description: 'Change Perspective...', subitems: openItems, icon: 'external-link-alt' });
+ };
render() {
- const dividerDragger = this.splitPercentage === 0 ? (null) :
- <div className="keyValueBox-dividerDragger" style={{ transform: `translate(calc(${100 - this.splitPercentage}% - 5px), 0px)` }}>
- <div className="keyValueBox-dividerDraggerThumb" onPointerDown={this.onDividerDown} />
- </div>;
-
- return (<div className="keyValueBox-cont" onWheel={this.onPointerWheel} onContextMenu={this.specificContextMenu} ref={this._mainCont}>
- <table className="keyValueBox-table">
- <tbody className="keyValueBox-tbody">
- <tr className="keyValueBox-header">
- <th className="keyValueBox-key" style={{ width: `${100 - this.splitPercentage}%` }} ref={this._keyHeader}
- onPointerDown={SetupDrag(this._keyHeader, this.getTemplate)}
- >Key</th>
- <th className="keyValueBox-fields" style={{ width: `${this.splitPercentage}%` }}>Fields</th>
- </tr>
- {this.createTable}
- {this.newKeyValue}
- </tbody>
- </table>
- {dividerDragger}
- </div>);
+ const dividerDragger =
+ this.splitPercentage === 0 ? null : (
+ <div className="keyValueBox-dividerDragger" style={{ transform: `translate(calc(${100 - this.splitPercentage}% - 5px), 0px)` }}>
+ <div className="keyValueBox-dividerDraggerThumb" onPointerDown={this.onDividerDown} />
+ </div>
+ );
+
+ return (
+ <div className="keyValueBox-cont" onWheel={this.onPointerWheel} onContextMenu={this.specificContextMenu} ref={this._mainCont}>
+ <table className="keyValueBox-table">
+ <tbody className="keyValueBox-tbody">
+ <tr className="keyValueBox-header">
+ <th className="keyValueBox-key" style={{ width: `${100 - this.splitPercentage}%` }} ref={this._keyHeader} onPointerDown={SetupDrag(this._keyHeader, this.getFieldView)}>
+ Key
+ </th>
+ <th className="keyValueBox-fields" style={{ width: `${this.splitPercentage}%` }}>
+ Fields
+ </th>
+ </tr>
+ {this.createTable}
+ {this.newKeyValue}
+ </tbody>
+ </table>
+ {dividerDragger}
+ </div>
+ );
}
}
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/LoadingBox.scss b/src/client/views/nodes/LoadingBox.scss
new file mode 100644
index 000000000..d63ed2575
--- /dev/null
+++ b/src/client/views/nodes/LoadingBox.scss
@@ -0,0 +1,35 @@
+.loadingBoxContainer {
+ display: flex;
+ flex-direction: column;
+ align-content: center;
+ justify-content: center;
+ background-color: #fdfdfd;
+ height: 100%;
+ align-items: center;
+}
+
+.textContainer {
+ margin: 5px;
+}
+
+.textContainer {
+ justify-content: center;
+ align-content: center;
+}
+
+.textContainer,
+.text {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 80%;
+ text-align: center;
+}
+
+.headerText {
+ text-align: center;
+ font-weight: bold;
+}
+
+.spinner {
+ text-align: center;
+}
diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx
new file mode 100644
index 000000000..acf728ebb
--- /dev/null
+++ b/src/client/views/nodes/LoadingBox.tsx
@@ -0,0 +1,47 @@
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import ReactLoading from 'react-loading';
+import { StrCast } from '../../../fields/Types';
+import { ViewBoxAnnotatableComponent } from '../DocComponent';
+import { FieldView, FieldViewProps } from './FieldView';
+import './LoadingBox.scss';
+
+/**
+ * LoadingBox Class represents a placeholder doc for documents that are currently
+ * being uploaded to the server and being fetched by the client. The LoadingBox doc is then used to
+ * generate the actual type of doc that is required once the document has been successfully uploaded.
+ *
+ * Design considerations:
+ * We are using the docToFiles map in Documents to keep track of all files being uploaded in one session of the client.
+ * If the file is not found we assume an error has occurred with the file upload, e.g. it has been interrupted by a client refresh
+ * or network issues. The docToFiles essentially gets reset everytime the page is refreshed.
+ *
+ * TODOs:
+ * 1) ability to query server to retrieve files that already exist if users upload duplicate files.
+ * 2) ability to restart upload if there is an error
+ * 3) detect network error and notify the user
+ * 4 )if file upload gets interrupted, it still gets uploaded to the server if there are no network interruptions which leads to unused space. this could be
+ * handled with (1)
+ * 5) Fixing the stacking view bug
+ * 6) Fixing the CSS
+ *
+ * @author naafiyan
+ */
+@observer
+export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
+ public static LayoutString(fieldKey: string) {
+ return FieldView.LayoutString(LoadingBox, fieldKey);
+ }
+
+ render() {
+ return (
+ <div className="loadingBoxContainer" style={{ background: this.rootDoc.isLoading ? '' : 'red' }}>
+ <div className="textContainer">
+ <p className="headerText">{this.rootDoc.isLoading ? 'Loading (can take several minutes):' : StrCast(this.rootDoc.errorMessage, 'Error Loading File:')}</p>
+ <span className="text">{StrCast(this.rootDoc.title)}</span>
+ {!this.rootDoc.isLoading ? null : <ReactLoading type={'spinningBubbles'} color={'blue'} height={100} width={100} />}
+ </div>
+ </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.scss b/src/client/views/nodes/VideoBox.scss
index aa51714da..c2aee7a1b 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -1,4 +1,4 @@
-@import "../global/globalCssVariables.scss";
+@import '../global/globalCssVariables.scss';
.mini-viewer {
cursor: grab;
@@ -97,7 +97,7 @@
height: 40px;
padding: 0 10px 0 7px;
transition: opacity 0.3s;
- z-index: 100001;
+ z-index: 10001;
.timecode-controls {
display: flex;
@@ -114,7 +114,8 @@
}
}
- .toolbar-slider.volume, .toolbar-slider.zoom {
+ .toolbar-slider.volume,
+ .toolbar-slider.zoom {
width: 50px;
}
@@ -157,7 +158,8 @@
}
}
-.videoBox-content-fullScreen, .videoBox-content-fullScreen-interactive {
+.videoBox-content-fullScreen,
+.videoBox-content-fullScreen-interactive {
display: flex;
justify-content: center;
align-items: flex-end;
@@ -175,16 +177,16 @@ video::-webkit-media-controls {
display: none !important;
}
-input[type="range"] {
+input[type='range'] {
-webkit-appearance: none;
background: none;
}
-input[type="range"]:focus {
+input[type='range']:focus {
outline: none;
}
-input[type="range"]::-webkit-slider-runnable-track {
+input[type='range']::-webkit-slider-runnable-track {
width: 100%;
height: 10px;
cursor: pointer;
@@ -193,7 +195,7 @@ input[type="range"]::-webkit-slider-runnable-track {
border-radius: 10px;
}
-input[type="range"]::-webkit-slider-thumb {
+input[type='range']::-webkit-slider-thumb {
box-shadow: 0;
border: 0;
height: 12px;
@@ -203,4 +205,4 @@ input[type="range"]::-webkit-slider-thumb {
cursor: pointer;
-webkit-appearance: none;
margin-top: -1px;
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index b1f7d8023..b45ee7f6e 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -396,10 +396,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// sets video info on load
videoLoad = action(() => {
- const aspect = this.player!.videoWidth / this.player!.videoHeight;
- Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth);
- Doc.SetNativeHeight(this.dataDoc, this.player!.videoHeight);
- this.layoutDoc._height = NumCast(this.layoutDoc._width) / aspect;
+ const aspect = this.player!.videoWidth / (this.player!.videoHeight || 1);
+ if (aspect) {
+ Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth);
+ Doc.SetNativeHeight(this.dataDoc, this.player!.videoHeight);
+ this.layoutDoc._height = NumCast(this.layoutDoc._width) / aspect;
+ }
if (Number.isFinite(this.player!.duration)) {
this.rawDuration = this.player!.duration;
} else this.rawDuration = NumCast(this.dataDoc[this.fieldKey + '-duration']);
@@ -418,31 +420,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)));
}
};
@@ -554,7 +551,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
return !field ? (
<div key="loading">Loading</div>
) : (
- <div className="videoBox-contentContainer" key="container" style={{ mixBlendMode: 'multiply', cursor: this._fullScreen && !this._controlsVisible ? 'none' : 'pointer' }}>
+ <div className="videoBox-contentContainer" key="container" style={{ mixBlendMode: 'multiply', cursor: this._fullScreen && !this._controlsVisible ? 'none' : 'default' }}>
<div className={classname} ref={this.setContentRef} onPointerDown={e => this._fullScreen && e.stopPropagation()}>
{this._fullScreen && (
<div
@@ -836,16 +833,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// stretches vertically or horizontally depending on video orientation so video fits full screen
fullScreenSize() {
if (this._videoRef && this._videoRef.videoHeight / this._videoRef.videoWidth > 1) {
- return { height: '100%' };
- } else {
- return { width: '100%' };
+ //prettier-ignore
+ return ({ height: '100%' });
}
+ //prettier-ignore
+ return ({ width: '100%' });
}
// for zoom slider, sets timeline waveform zoom
- zoom = (zoom: number) => {
- this.timeline?.setZoom(zoom);
- };
+ zoom = (zoom: number) => this.timeline?.setZoom(zoom);
// plays link
playLink = (doc: Doc) => {
@@ -859,7 +855,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// starts marquee selection
marqueeDown = (e: React.PointerEvent) => {
- if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) {
+ if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._viewScale, 1) === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) {
setupMoveUpEvents(
this,
e,
@@ -898,7 +894,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,140 +907,46 @@ 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`];
// renders video controls
componentUI = (boundsLeft: number, boundsTop: number) => {
- const bounds = this.props.docViewPath().lastElement().getBounds();
- const left = bounds?.left || 0;
- const right = bounds?.right || 0;
- const top = bounds?.top || 0;
- const height = (bounds?.bottom || 0) - top;
- const width = Math.max(right - left, 100);
- const uiHeight = Math.max(25, Math.min(50, height / 10));
- const uiMargin = Math.min(10, height / 20);
- const vidHeight = (height * this.heightPercent) / 100;
- const yPos = top + vidHeight - uiHeight - uiMargin;
- const xPos = uiHeight / vidHeight > 0.4 ? right + 10 : left + 10;
+ const xf = this.props.ScreenToLocalTransform().inverse();
+ const height = this.props.PanelHeight();
+ const vidHeight = (height * this.heightPercent) / 100 / this.scaling();
+ const vidWidth = this.props.PanelWidth() / this.scaling();
+ const uiHeight = 25;
+ const uiMargin = 10;
+ const yBot = xf.transformPoint(0, vidHeight)[1];
+ // prettier-ignore
+ const yMid = (xf.transformPoint(0, 0)[1] +
+ xf.transformPoint(0, height / this.scaling())[1]) / 2;
+ const xPos = xf.transformPoint(vidWidth / 2, 0)[0];
+ const xRight = xf.transformPoint(vidWidth, 0)[0];
const opacity = this._scrubbing ? 0.3 : this._controlsVisible ? 1 : 0;
- return this._fullScreen || right - left < 50 ? null : (
+ return this._fullScreen || (xRight - xPos) * 2 < 50 ? null : (
<div className="videoBox-ui-wrapper" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
- <div className="videoBox-ui" style={{ left: xPos, top: yPos, height: uiHeight, width: width - 20, transition: this._clicking ? 'top 0.5s' : '', opacity: opacity }}>
+ <div
+ className="videoBox-ui"
+ style={{
+ transformOrigin: 'top left',
+ transform: `rotate(${NumCast(this.rootDoc.jitterRotation)}deg) translate(${-(xRight - xPos) + 10}px, ${yBot - yMid - uiHeight - uiMargin}px)`,
+ left: xPos,
+ top: yMid,
+ height: uiHeight,
+ width: (xRight - xPos) * 2 - 20,
+ transition: this._clicking ? 'top 0.5s' : '',
+ opacity,
+ }}>
{this.UIButtons}
</div>
</div>
);
};
- @computed get UIButtons() {
- const bounds = this.props.docViewPath().lastElement().getBounds();
- const width = (bounds?.right || 0) - (bounds?.left || 0);
- const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0);
- return (
- <>
- <div className="videobox-button" title={this._playing ? 'play' : 'pause'} onPointerDown={this.onPlayDown}>
- <FontAwesomeIcon icon={this._playing ? 'pause' : 'play'} />
- </div>
-
- {this.timeline && width > 150 && (
- <div className="timecode-controls">
- <div className="timecode-current">{formatTime(curTime)}</div>
-
- {this._fullScreen || (this.heightPercent === 100 && width > 200) ? (
- <div className="timeline-slider">
- <input
- type="range"
- step="0.1"
- min={this.timeline.clipStart}
- max={this.timeline.clipEnd}
- value={curTime}
- className="toolbar-slider time-progress"
- onPointerDown={action((e: React.PointerEvent) => {
- e.stopPropagation();
- this._scrubbing = true;
- })}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setPlayheadTime(Number(e.target.value))}
- onPointerUp={action((e: React.PointerEvent) => {
- e.stopPropagation();
- this._scrubbing = false;
- })}
- />
- </div>
- ) : (
- <div>/</div>
- )}
-
- <div className="timecode-end">{formatTime(this.timeline.clipDuration)}</div>
- </div>
- )}
-
- <div className="videobox-button" title={'full screen'} onPointerDown={this.onFullDown}>
- <FontAwesomeIcon icon="expand" />
- </div>
-
- {!this._fullScreen && width > 300 && (
- <div className="videobox-button" title={'show timeline'} onPointerDown={this.onTimelineHdlDown}>
- <FontAwesomeIcon icon="eye" />
- </div>
- )}
-
- {!this._fullScreen && width > 300 && (
- <div className="videobox-button" title={this.timeline?.IsTrimming !== TrimScope.None ? 'finish trimming' : 'start trim'} onPointerDown={this.onClipPointerDown}>
- <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? 'check' : 'cut'} />
- </div>
- )}
-
- <div
- className="videobox-button"
- title={this._muted ? 'unmute' : 'mute'}
- onPointerDown={e => {
- e.stopPropagation();
- this.toggleMute();
- }}>
- <FontAwesomeIcon icon={this._muted ? 'volume-mute' : 'volume-up'} />
- </div>
- {width > 300 && (
- <input
- type="range"
- style={{ width: `min(25%, 50px)` }}
- step="0.1"
- min="0"
- max="1"
- value={this._muted ? 0 : this._volume}
- className="toolbar-slider volume"
- onPointerDown={(e: React.PointerEvent) => e.stopPropagation()}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))}
- />
- )}
-
- {!this._fullScreen && this.heightPercent !== 100 && width > 300 && (
- <>
- <div className="videobox-button" title="zoom">
- <FontAwesomeIcon icon="search-plus" />
- </div>
- <input
- type="range"
- step="0.1"
- min="1"
- max="5"
- value={this.timeline?._zoomFactor}
- className="toolbar-slider zoom"
- onPointerDown={(e: React.PointerEvent) => {
- e.stopPropagation();
- }}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
- this.zoom(Number(e.target.value));
- }}
- />
- </>
- )}
- </>
- );
- }
-
// renders CollectionStackedTimeline
@computed get renderTimeline() {
return (
@@ -1124,7 +1026,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}
@@ -1153,6 +1055,112 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
</div>
);
}
+
+ @computed get UIButtons() {
+ const bounds = this.props.docViewPath().lastElement().getBounds();
+ const width = (bounds?.right || 0) - (bounds?.left || 0);
+ const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0);
+ return (
+ <>
+ <div className="videobox-button" title={this._playing ? 'play' : 'pause'} onPointerDown={this.onPlayDown}>
+ <FontAwesomeIcon icon={this._playing ? 'pause' : 'play'} />
+ </div>
+
+ {this.timeline && width > 150 && (
+ <div className="timecode-controls">
+ <div className="timecode-current">{formatTime(curTime)}</div>
+
+ {this._fullScreen || (this.heightPercent === 100 && width > 200) ? (
+ <div className="timeline-slider">
+ <input
+ type="range"
+ step="0.1"
+ min={this.timeline.clipStart}
+ max={this.timeline.clipEnd}
+ value={curTime}
+ className="toolbar-slider time-progress"
+ onPointerDown={action((e: React.PointerEvent) => {
+ e.stopPropagation();
+ this._scrubbing = true;
+ })}
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setPlayheadTime(Number(e.target.value))}
+ onPointerUp={action((e: React.PointerEvent) => {
+ e.stopPropagation();
+ this._scrubbing = false;
+ })}
+ />
+ </div>
+ ) : (
+ <div>/</div>
+ )}
+
+ <div className="timecode-end">{formatTime(this.timeline.clipDuration)}</div>
+ </div>
+ )}
+
+ <div className="videobox-button" title={'full screen'} onPointerDown={this.onFullDown}>
+ <FontAwesomeIcon icon="expand" />
+ </div>
+
+ {!this._fullScreen && width > 300 && (
+ <div className="videobox-button" title={'show timeline'} onPointerDown={this.onTimelineHdlDown}>
+ <FontAwesomeIcon icon="eye" />
+ </div>
+ )}
+
+ {!this._fullScreen && width > 300 && (
+ <div className="videobox-button" title={this.timeline?.IsTrimming !== TrimScope.None ? 'finish trimming' : 'start trim'} onPointerDown={this.onClipPointerDown}>
+ <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? 'check' : 'cut'} />
+ </div>
+ )}
+
+ <div
+ className="videobox-button"
+ title={this._muted ? 'unmute' : 'mute'}
+ onPointerDown={e => {
+ e.stopPropagation();
+ this.toggleMute();
+ }}>
+ <FontAwesomeIcon icon={this._muted ? 'volume-mute' : 'volume-up'} />
+ </div>
+ {width > 300 && (
+ <input
+ type="range"
+ style={{ width: `min(25%, 50px)` }}
+ step="0.1"
+ min="0"
+ max="1"
+ value={this._muted ? 0 : this._volume}
+ className="toolbar-slider volume"
+ onPointerDown={(e: React.PointerEvent) => e.stopPropagation()}
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))}
+ />
+ )}
+
+ {!this._fullScreen && this.heightPercent !== 100 && width > 300 && (
+ <>
+ <div className="videobox-button" title="zoom">
+ <FontAwesomeIcon icon="search-plus" />
+ </div>
+ <input
+ type="range"
+ step="0.1"
+ min="1"
+ max="5"
+ value={this.timeline?._zoomFactor}
+ className="toolbar-slider zoom"
+ onPointerDown={(e: React.PointerEvent) => {
+ e.stopPropagation();
+ }}
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+ this.zoom(Number(e.target.value));
+ }}
+ />
+ </>
+ )}
+ </>
+ );
+ }
}
VideoBox._nativeControls = false;
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index d8dd074a5..85986ff27 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -1,9 +1,11 @@
-@import "../global/globalCssVariables.scss";
-
+@import '../global/globalCssVariables.scss';
.webBox {
height: 100%;
- position: relative;
+ width: 100%;
+ top: 0;
+ left: 0;
+ position: absolute;
display: flex;
.webBox-sideResizer {
@@ -84,7 +86,6 @@
background: none;
}
-
.webBox-overlayCont {
position: absolute;
width: calc(100% - 40px);
@@ -95,7 +96,7 @@
justify-content: center;
align-items: center;
overflow: hidden;
- transition: left .5s;
+ transition: left 0.5s;
pointer-events: all;
.webBox-searchBar {
@@ -158,7 +159,7 @@
left: 0;
cursor: text;
padding: 15px;
- height: 100%
+ height: 100%;
}
.webBox-cont {
@@ -235,7 +236,7 @@
height: 25px;
align-items: center;
- >svg {
+ > svg {
margin: auto;
}
}
@@ -257,4 +258,4 @@
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index d97277c2b..2c5deba88 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,5 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx';
import { observer } from 'mobx-react';
import * as WebRequest from 'web-request';
import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
@@ -11,11 +11,11 @@ import { listSpec } from '../../../fields/Schema';
import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField, WebField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
+import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils';
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';
@@ -35,6 +35,7 @@ import { LinkDocPreview } from './LinkDocPreview';
import { VideoBox } from './VideoBox';
import './WebBox.scss';
import React = require('react');
+import { DragManager } from '../../util/DragManager';
const { CreateImage } = require('./WebBoxRenderer');
const _global = (window /* browser */ || global) /* node */ as any;
const htmlToText = require('html-to-text');
@@ -134,25 +135,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 +206,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,10 +282,10 @@ 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;
+ const focusSpeed = smooth ? NumCast(doc.focusSpeed, 500) : 0;
this.goTo(scrollTo, focusSpeed);
return focusSpeed;
} else if (!this._webPageHasBeenRendered || !this.getScrollHeight() || this._initialScroll !== undefined) {
@@ -310,7 +316,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 +328,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 +610,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 +714,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 +723,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 +737,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) => {
@@ -790,6 +819,56 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
}
+ @computed get webpage() {
+ const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
+ const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
+ const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
+ const renderAnnotations = (docFilters?: () => string[]) => (
+ <CollectionFreeFormView
+ {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+ renderDepth={this.props.renderDepth + 1}
+ isAnnotationOverlay={true}
+ fieldKey={this.annotationKey}
+ CollectionView={undefined}
+ setPreviewCursor={this.setPreviewCursor}
+ PanelWidth={this.panelWidth}
+ PanelHeight={this.panelHeight}
+ ScreenToLocalTransform={this.scrollXf}
+ NativeDimScaling={returnOne}
+ dropAction={'alias'}
+ docFilters={docFilters}
+ select={emptyFunction}
+ bringToFront={emptyFunction}
+ styleProvider={this.childStyleProvider}
+ whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+ removeDocument={this.removeDocument}
+ moveDocument={this.moveDocument}
+ addDocument={this.addDocument}
+ childPointerEvents={this.props.isContentActive() ? 'all' : undefined}
+ pointerEvents={this.annotationPointerEvents}
+ />
+ );
+ return (
+ <div
+ className={'webBox-outerContent'}
+ ref={this._outerRef}
+ style={{
+ height: `${100 / scale}%`,
+ pointerEvents,
+ }}
+ onWheel={StopEvent} // block wheel events from propagating since they're handled by the iframe
+ onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)}
+ onPointerDown={this.onMarqueeDown}>
+ <div className={'webBox-innerContent'} style={{ height: this._webPageHasBeenRendered ? NumCast(this.scrollHeight, 50) : '100%', pointerEvents }}>
+ {this.content}
+ {<div style={{ display: DragManager.docsBeingDragged.length ? 'none' : undefined, mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>}
+ {renderAnnotations(this.opaqueFilter)}
+ {this.annotationLayer}
+ </div>
+ </div>
+ );
+ }
+
@computed get searchUI() {
return (
<div className="webBox-ui" onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? 'flex' : 'none' }}>
@@ -827,13 +906,12 @@ 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')];
transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
- opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
+ opaqueFilter = () => [...this.props.docFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])];
childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
if (doc instanceof Doc && property === StyleProp.PointerEvents) {
if (doc.textInlineAnnotations) return 'none';
@@ -841,40 +919,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return this.props.styleProvider?.(doc, props, property);
};
pointerEvents = () => (!this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none');
- annotationPointerEvents = () => (this._isAnnotating || SnappingManager.GetIsDragging() ? 'all' : 'none');
+ annotationPointerEvents = () => (this._isAnnotating || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None ? 'all' : 'none');
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 renderAnnotations = (docFilters?: () => string[]) => (
- <CollectionFreeFormView
- {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
- renderDepth={this.props.renderDepth + 1}
- isAnnotationOverlay={true}
- fieldKey={this.annotationKey}
- CollectionView={undefined}
- setPreviewCursor={this.setPreviewCursor}
- PanelWidth={this.panelWidth}
- PanelHeight={this.panelHeight}
- ScreenToLocalTransform={this.scrollXf}
- scaling={returnOne}
- dropAction={'alias'}
- docFilters={docFilters || this.basicFilter}
- dontRenderDocuments={docFilters ? false : true}
- select={emptyFunction}
- ContentScaling={returnOne}
- bringToFront={emptyFunction}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- removeDocument={this.removeDocument}
- moveDocument={this.moveDocument}
- addDocument={this.addDocument}
- styleProvider={this.childStyleProvider}
- childPointerEvents={this.props.isContentActive() ? 'all' : undefined}
- pointerEvents={this.annotationPointerEvents}
- />
- );
+ const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
+ const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
return (
- <div className="webBox" ref={this._mainCont} style={{ pointerEvents: this.pointerEvents(), display: this.props.thumbShown?.() ? 'none' : undefined }}>
+ <div className="webBox" ref={this._mainCont} style={{ pointerEvents: this.pointerEvents(), display: !SnappingManager.GetIsDragging() && this.props.thumbShown?.() ? 'none' : undefined }}>
<div className="webBox-background" style={{ backgroundColor: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor) }} />
<div
className="webBox-container"
@@ -884,27 +935,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
pointerEvents,
}}
onContextMenu={this.specificContextMenu}>
- <div
- className={'webBox-outerContent'}
- ref={this._outerRef}
- style={{
- height: `${100 / scale}%`,
- pointerEvents,
- }}
- onWheel={e => {
- e.stopPropagation();
- e.preventDefault();
- }} // block wheel events from propagating since they're handled by the iframe
- onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)}
- onPointerDown={this.onMarqueeDown}>
- <div className={'webBox-innerContent'} style={{ height: this._webPageHasBeenRendered ? NumCast(this.scrollHeight, 50) : '100%', pointerEvents }}>
- {this.content}
- <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
- {renderAnnotations(this.opaqueFilter)}
- {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
- {this.annotationLayer}
- </div>
- </div>
+ {this.webpage}
{!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
<div style={{ transformOrigin: 'top left', transform: `scale(${1 / scale})` }}>
<MarqueeAnnotator
@@ -934,33 +965,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..4a6099fb3 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -1,7 +1,8 @@
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 Color from 'color';
+import { action, computed, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ColorState, SketchPicker } from 'react-color';
@@ -21,7 +22,7 @@ import { DocComponent } from '../../DocComponent';
import { EditableView } from '../../EditableView';
import { GestureOverlay } from '../../GestureOverlay';
import { Colors } from '../../global/globalEnums';
-import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
+import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, SetActiveIsInkMask } from '../../InkingStroke';
import { InkTranscription } from '../../InkTranscription';
import { StyleProp } from '../../StyleProvider';
import { FieldView, FieldViewProps } from '.././FieldView';
@@ -86,11 +87,23 @@ 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));
+ }
+ Icon = (color: string) => {
+ const icon = StrCast(this.dataDoc.icon, 'user') as any;
+ const trailsIcon = () => <img src={`/assets/${'presTrails.png'}`} style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? '0%' : '100%'})` }} />;
+ return !icon ? null : icon === 'pres-trail' ? trailsIcon() : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />;
+ };
+ @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:
@@ -217,7 +230,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
className={`menuButton ${this.type} ${active}`}
style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
onClick={action(() => (this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen))}>
- <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+ {this.Icon(color)}
{!this.label || !FontIconBox.GetShowLabels() ? null : (
<div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}>
{' '}
@@ -241,16 +254,13 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
const script = ScriptCast(this.rootDoc.script);
- if (!script) {
- return null;
- }
let noviceList: string[] = [];
let text: string | undefined;
let dropdown = true;
let icon: IconProp = 'caret-down';
try {
- if (script.script.originalScript.startsWith('setView')) {
+ if (script?.script.originalScript.startsWith('setView')) {
const selected = SelectionManager.Docs().lastElement();
if (selected) {
if (StrCast(selected.type) === DocumentType.COL) {
@@ -266,7 +276,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
text = 'User Default';
}
noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking];
- } else if (script.script.originalScript.startsWith('setFont')) {
+ } else if (script?.script.originalScript.startsWith('setFont')) {
const editorView = RichTextMenu.Instance?.TextView?.EditorView;
text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
noviceList = ['Roboto', 'Times New Roman', 'Arial', 'Georgia', 'Comic Sans MS', 'Tahoma', 'Impact', 'Crimson Text'];
@@ -276,11 +286,9 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
}
// Get items to place into the list
- const list = this.buttonList.map(value => {
- if (Doc.noviceMode && !noviceList.includes(value)) {
- return;
- }
- return (
+ const list = this.buttonList
+ .filter(value => !Doc.noviceMode || noviceList.includes(value))
+ .map(value => (
<div
className="list-item"
key={`${value}`}
@@ -291,8 +299,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
onClick={() => script.script.run({ value }).result}>
{value[0].toUpperCase() + value.slice(1)}
</div>
- );
- });
+ ));
const label =
!this.label || !FontIconBox.GetShowLabels() ? null : (
@@ -373,7 +380,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
onClick={action(() => (this.colorPickerClosed = !this.colorPickerClosed))}
onPointerDown={e => e.stopPropagation()}>
- <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+ {this.Icon(color)}
<div className="colorButton-color" style={{ backgroundColor: curColor }} />
{label}
{/* {dropdownCaret} */}
@@ -425,7 +432,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
} else {
return (
<div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')}`} style={{ opacity: 1, backgroundColor, color }}>
- <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+ {this.Icon(color)}
{label}
</div>
);
@@ -438,11 +445,10 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
@computed get defaultButton() {
const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
- const active: string = StrCast(this.rootDoc.dropDownOpen);
return (
<div className={`menuButton ${this.type}`} onContextMenu={this.specificContextMenu} style={{ backgroundColor: 'transparent', borderBottomLeftRadius: this.dropdown ? 0 : undefined }}>
<div className="menuButton-wrap">
- <FontAwesomeIcon className={`menuButton-icon-${this.type}`} icon={this.icon} color={'black'} size={'sm'} />
+ {this.Icon(color)}
{!this.label || !FontIconBox.GetShowLabels() ? null : (
<div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}>
{' '}
@@ -474,90 +480,50 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
render() {
const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
- const label =
- !this.label || !FontIconBox.GetShowLabels() ? null : (
- <div className="fontIconBox-label" style={{ color, backgroundColor }}>
- {this.label}
- </div>
- );
-
- const menuLabel =
+ const label = (noBackground: boolean = false) =>
!this.label || !FontIconBox.GetShowLabels() ? null : (
- <div className="fontIconBox-label" style={{ color, backgroundColor: 'transparent' }}>
+ <div className="fontIconBox-label" style={{ color, backgroundColor: noBackground ? 'transparent' : backgroundColor }}>
{this.label}
</div>
);
-
- const buttonText = StrCast(this.rootDoc.buttonText);
-
// TODO:glr Add label of button type
- let button: JSX.Element | null = this.defaultButton;
+ let button: JSX.Element = this.defaultButton;
+ // prettier-ignore
switch (this.type) {
- case ButtonType.TextButton:
- button = (
+ case ButtonType.DropdownList: return this.dropdownListButton;
+ case ButtonType.ColorButton: return this.colorButton;
+ case ButtonType.NumberButton: return this.numberButton;
+ case ButtonType.EditableText: return this.editableText;
+ case ButtonType.DropdownButton: button = this.dropdownButton; break;
+ case ButtonType.ToggleButton: button = this.toggleButton; break;
+ case ButtonType.TextButton: button = (
<div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}>
- <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
- {buttonText ? <div className="button-text">{buttonText}</div> : null}
- {label}
- </div>
- );
- // button = <TextButton {...buttonProps}></TextButton>
- break;
- case ButtonType.EditableText:
- button = this.editableText;
- break;
- case ButtonType.NumberButton:
- button = this.numberButton;
- break;
- case ButtonType.DropdownButton:
- button = this.dropdownButton;
- break;
- case ButtonType.DropdownList:
- button = this.dropdownListButton;
- break;
- case ButtonType.ColorButton:
- button = this.colorButton;
- break;
- case ButtonType.ToolButton:
- button = (
- <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')}`} style={{ opacity: 1, backgroundColor, color }}>
- <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
- {label}
+ {this.Icon(color)}
+ {StrCast(this.rootDoc.buttonText) ? <div className="button-text">{StrCast(this.rootDoc.buttonText)}</div> : null}
+ {label()}
</div>
);
break;
- case ButtonType.ToggleButton:
- button = this.toggleButton;
- // button = <ToggleButton {...buttonProps}></ToggleButton>
- break;
case ButtonType.ClickButton:
- button = (
- <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')}`} style={{ color, backgroundColor, opacity: 1 }}>
- <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
- {label}
+ case ButtonType.ToolButton: button = (
+ <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')}`} style={{ backgroundColor, color, opacity: 1 }}>
+ {this.Icon(color)}
+ {label()}
</div>
);
break;
- case ButtonType.MenuButton:
- const trailsIcon = <img src={`/assets/${'presTrails.png'}`} style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? '0%' : '100%'})` }} />;
- button = (
+ case ButtonType.MenuButton: button = (
<div className={`menuButton ${this.type}`} style={{ color, backgroundColor }}>
- {this.icon === 'pres-trail' ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />}
- {menuLabel}
+ {this.Icon(color)}
+ {label(true)}
<FontIconBadge value={Cast(this.Document.badgeValue, 'string', null)} />
</div>
);
break;
- default:
- break;
}
- return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? (
- button
- ) : button !== null ? (
- <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>{button}</Tooltip>
- ) : null;
+ return !this.layoutDoc.toolTip ? button : <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>{button}</Tooltip>;
}
}
@@ -569,11 +535,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
@@ -610,27 +576,19 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
// toggle: Set overlay status of selected document
ScriptingGlobals.add(function setFont(font: string, checkResult?: boolean) {
- SelectionManager.Docs().map(doc => (doc._fontFamily = font));
- const editorView = RichTextMenu.Instance.TextView?.EditorView;
- if (checkResult) {
- return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
- }
- if (editorView) RichTextMenu.Instance.setFontFamily(font);
- else Doc.UserDoc().fontFamily = font;
+ if (checkResult) return RichTextMenu.Instance?.fontFamily;
+ font && RichTextMenu.Instance.setFontFamily(font);
});
ScriptingGlobals.add(function getActiveTextInfo(info: 'family' | 'size' | 'color' | 'highlight') {
const editorView = RichTextMenu.Instance.TextView?.EditorView;
const style = editorView?.state && RichTextMenu.Instance.getActiveFontStylesOnSelection();
+ // prettier-ignore
switch (info) {
- case 'family':
- return style?.activeFamilies[0];
- case 'size':
- return style?.activeSizes[0];
- case 'color':
- return style?.activeColors[0];
- case 'highlight':
- return style?.activeHighlights[0];
+ case 'family': return style?.activeFamilies[0];
+ case 'size': return style?.activeSizes[0];
+ case 'color': return style?.activeColors[0];
+ case 'highlight': return style?.activeHighlights[0];
}
});
@@ -655,14 +613,8 @@ ScriptingGlobals.add(function setBulletList(mapStyle: 'bullet' | 'decimal', chec
// toggle: Set overlay status of selected document
ScriptingGlobals.add(function setFontColor(color?: string, checkResult?: boolean) {
- const editorView = RichTextMenu.Instance?.TextView?.EditorView;
-
- if (checkResult) {
- return editorView ? RichTextMenu.Instance.fontColor : Doc.UserDoc().fontColor;
- }
-
- if (editorView) color && RichTextMenu.Instance.setColor(color, editorView, editorView?.dispatch);
- else Doc.UserDoc().fontColor = color;
+ if (checkResult) return RichTextMenu.Instance.fontColor;
+ color && RichTextMenu.Instance.setColor(color);
});
// toggle: Set overlay status of selected document
@@ -684,14 +636,12 @@ ScriptingGlobals.add(function setFontHighlight(color?: string, checkResult?: boo
// toggle: Set overlay status of selected document
ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: boolean) {
- const editorView = RichTextMenu.Instance?.TextView?.EditorView;
if (checkResult) {
- return RichTextMenu.Instance.fontSize.replace('px', '');
+ return RichTextMenu.Instance?.fontSize.replace('px', '');
}
if (typeof size === 'number') size = size.toString();
if (size && Number(size).toString() === size) size += 'px';
- if (editorView) RichTextMenu.Instance.setFontSize(size);
- else Doc.UserDoc()._fontSize = size;
+ RichTextMenu.Instance.setFontSize(size);
});
ScriptingGlobals.add(function toggleNoAutoLinkAnchor(checkResult?: boolean) {
const editorView = RichTextMenu.Instance?.TextView?.EditorView;
@@ -700,6 +650,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;
@@ -844,6 +801,21 @@ ScriptingGlobals.add(function setActiveTool(tool: string, checkResult?: boolean)
});
// toggle: Set overlay status of selected document
+ScriptingGlobals.add(function setIsInkMask(checkResult?: boolean) {
+ const selected = SelectionManager.Docs().lastElement();
+ if (checkResult) {
+ if (selected?.type === DocumentType.INK) {
+ return BoolCast(selected.isInkMask) ? Colors.MEDIUM_BLUE : 'transparent';
+ }
+ return ActiveIsInkMask() ? Colors.MEDIUM_BLUE : 'transparent';
+ }
+ SetActiveIsInkMask(!ActiveIsInkMask());
+ SelectionManager.Docs()
+ .filter(doc => doc.type === DocumentType.INK)
+ .map(doc => (doc.isInkMask = !doc.isInkMask));
+});
+
+// toggle: Set overlay status of selected document
ScriptingGlobals.add(function setFillColor(color?: string, checkResult?: boolean) {
const selected = SelectionManager.Docs().lastElement();
if (checkResult) {
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..35d919f38 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -178,7 +178,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
} else {
if (Number(newText).toString() === newText) {
if (this._fieldKey.startsWith('_')) Doc.Layout(this._textBoxDoc)[this._fieldKey] = Number(newText);
- Doc.SetInPlace(this._dashDoc!, this._fieldKey, newText, true);
+ Doc.SetInPlace(this._dashDoc!, this._fieldKey, Number(newText), true);
} else {
const splits = newText.split(DashFieldViewInternal.multiValueDelimeter);
if (this._fieldKey !== 'PARAMS' || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS) {
@@ -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..5cb805f80 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -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';
@@ -35,6 +35,7 @@ import { SnappingManager } from '../../../util/SnappingManager';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView';
import { CollectionStackingView } from '../../collections/CollectionStackingView';
+import { CollectionTreeView } from '../../collections/CollectionTreeView';
import { ContextMenu } from '../../ContextMenu';
import { ContextMenuProps } from '../../ContextMenuItem';
import { ViewBoxAnnotatableComponent } from '../../DocComponent';
@@ -44,6 +45,7 @@ import { LightboxView } from '../../LightboxView';
import { AnchorMenu } from '../../pdf/AnchorMenu';
import { SidebarAnnos } from '../../SidebarAnnos';
import { StyleProp } from '../../StyleProvider';
+import { DocumentViewInternal } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
import { LinkDocPreview } from '../LinkDocPreview';
import { DashDocCommentView } from './DashDocCommentView';
@@ -63,20 +65,12 @@ import applyDevTools = require('prosemirror-dev-tools');
import React = require('react');
const translateGoogleApi = require('translate-google-api');
-export interface FormattedTextBoxProps {
- makeLink?: () => Opt<Doc>; // bcz: hack: notifies the text document when the container has made a link. allows the text doc to react and setup a hyeprlink for any selected text
- xPadding?: number; // used to override document's settings for xMargin --- see CollectionCarouselView
- yPadding?: number;
- noSidebar?: boolean;
- dontScale?: boolean;
- dontSelectOnLoad?: boolean; // suppress selecting the text box when loaded (and mark as not being associated with scrollTop document field)
-}
export const GoogleRef = 'googleDocId';
type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
@observer
-export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps & FormattedTextBoxProps>() {
+export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
public static LayoutString(fieldStr: string) {
return FieldView.LayoutString(FormattedTextBox, fieldStr);
}
@@ -123,6 +117,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 +241,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 +384,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 +395,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 +428,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 +638,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 +694,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 +715,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
})
);
+ e.preventDefault();
e.stopPropagation();
return;
}
@@ -751,7 +774,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 +855,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 +984,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 +1005,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 }
);
@@ -1357,10 +1387,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
selectOnLoad && this._editorView!.focus();
// add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
- if (this._editorView && !this._editorView.state.storedMarks?.some(mark => mark.type === schema.marks.user_mark)) {
+ if (this._editorView) {
this._editorView.state.storedMarks = [
...(this._editorView.state.storedMarks ?? []),
- schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }),
+ ...(!this._editorView.state.storedMarks?.some(mark => mark.type === schema.marks.user_mark) ? [schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })] : []),
...(Doc.UserDoc().fontColor !== 'transparent' && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []),
...(Doc.UserDoc().fontStyle === 'italics' ? [schema.mark(schema.marks.em)] : []),
...(Doc.UserDoc().textDecoration === 'underline' ? [schema.mark(schema.marks.underline)] : []),
@@ -1436,14 +1466,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 +1509,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 +1597,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)) {
@@ -1666,12 +1697,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined;
if (children) {
- const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace('px', '')), margins);
+ const proseHeight = !this.ProseRef
+ ? 0
+ : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace('px', '')) + Number(getComputedStyle(child).marginTop.replace('px', '')) + Number(getComputedStyle(child).marginBottom.replace('px', '')), margins);
const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
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 +1713,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 +1727,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 +1758,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 +1768,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 +1777,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 +1788,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 +1827,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);
@@ -1791,18 +1836,27 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const paddingY = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
const selPad = (selected && !this.layoutDoc._singleLine) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0;
const selPaddingClass = selected && !this.layoutDoc._singleLine && paddingY >= 10 ? '-selected' : '';
- const styleFromString = this.styleFromLayoutString(scale); // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._headerHeight}px' >
- return styleFromString?.height === '0px' ? null : (
+ const styleFromLayoutString = Doc.styleFromLayoutString(this.rootDoc, this.layoutDoc, this.props, scale); // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._headerHeight}px' >
+ return styleFromLayoutString?.height === '0px' ? null : (
<div
className="formattedTextBox-cont"
onWheel={e => this.props.isContentActive() && e.stopPropagation()}
style={{
- transform: this.props.dontScale ? undefined : `scale(${scale})`,
- transformOrigin: this.props.dontScale ? undefined : 'top left',
- width: this.props.dontScale ? undefined : `${100 / scale}%`,
- height: this.props.dontScale ? undefined : `${100 / scale}%`,
+ ...(this.props.dontScale
+ ? {}
+ : {
+ transform: `scale(${scale})`,
+ transformOrigin: 'top left',
+ width: `${100 / scale}%`,
+ height: `${100 / scale}%`,
+ }),
+ transition: 'inherit',
// overflowY: this.layoutDoc._autoHeight ? "hidden" : undefined,
- ...styleFromString,
+ color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
+ fontSize: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize),
+ fontFamily: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontFamily),
+ fontWeight: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontWeight),
+ ...styleFromLayoutString,
}}>
<div
className={`formattedTextBox-cont`}
@@ -1810,11 +1864,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
style={{
overflow: this.autoHeight ? 'hidden' : undefined,
height: this.props.height || (this.autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : undefined),
- background: this.props.background ? this.props.background : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
- color: this.props.color ? this.props.color : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
- fontSize: this.props.fontSize ? this.props.fontSize : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize),
- fontWeight: Cast(this.layoutDoc._fontWeight, 'string', null) as any,
- fontFamily: StrCast(this.layoutDoc._fontFamily, 'inherit'),
pointerEvents: interactive ? undefined : 'none',
}}
onContextMenu={this.specificContextMenu}
@@ -1832,7 +1881,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 +1898,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..6c6d26af5 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -16,7 +16,7 @@ import { SelectionManager } from '../../../util/SelectionManager';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
import { FieldViewProps } from '../FieldView';
-import { FormattedTextBox, FormattedTextBoxProps } from './FormattedTextBox';
+import { FormattedTextBox } from './FormattedTextBox';
import { updateBullets } from './ProsemirrorExampleTransfer';
import './RichTextMenu.scss';
import { schema } from './schema_rts';
@@ -29,7 +29,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
private _linkToRef = React.createRef<HTMLInputElement>();
@observable public view?: EditorView;
- public editorProps: (FieldViewProps & FormattedTextBoxProps) | undefined;
+ public editorProps: FieldViewProps | undefined;
public _brushMap: Map<string, Set<Mark>> = new Map();
@@ -60,26 +60,16 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@observable private showLinkDropdown: boolean = false;
_reaction: IReactionDisposer | undefined;
- _delayHide = false;
constructor(props: Readonly<{}>) {
super(props);
runInAction(() => {
RichTextMenu.Instance = this;
+ this.updateMenu(undefined, undefined, props);
this._canFade = false;
this.Pinned = true;
});
}
- componentDidMount() {
- this._reaction = reaction(
- () => SelectionManager.Views(),
- () => this._delayHide && !(this._delayHide = false) && this.fadeOut(true)
- );
- }
- componentWillUnmount() {
- this._reaction?.();
- }
-
@computed get noAutoLink() {
return this._noLinkActive;
}
@@ -108,21 +98,18 @@ 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) {
return;
}
this.view = view;
- if (!view || !view.hasFocus()) {
- return;
- }
props && (this.editorProps = props);
// Don't do anything if the document/selection didn't change
- if (lastState?.doc.eq(view.state.doc) && lastState.selection.eq(view.state.selection)) return;
+ if (view && view.hasFocus()) {
+ if (lastState?.doc.eq(view.state.doc) && lastState.selection.eq(view.state.selection)) return;
+ }
// update active marks
const activeMarks = this.getActiveMarksOnSelection();
@@ -137,9 +124,9 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this.activeListType = this.getActiveListStyle();
this._activeAlignment = this.getActiveAlignment();
- this._activeFontFamily = !activeFamilies.length ? 'Arial' : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various';
- this._activeFontSize = !activeSizes.length ? StrCast(this.TextView.Document.fontSize, StrCast(Doc.UserDoc().fontSize, '10px')) : activeSizes[0];
- this._activeFontColor = !activeColors.length ? 'black' : activeColors.length > 0 ? String(activeColors[0]) : '...';
+ this._activeFontFamily = !activeFamilies.length ? StrCast(this.TextView?.Document.fontFamily, StrCast(Doc.UserDoc().fontFamily, 'Arial')) : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various';
+ this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, StrCast(Doc.UserDoc().fontSize, '10px')) : activeSizes[0];
+ this._activeFontColor = !activeColors.length ? StrCast(this.TextView?.Document.fontColor, StrCast(Doc.UserDoc().fontColor, 'black')) : activeColors.length > 0 ? String(activeColors[0]) : '...';
this.activeHighlightColor = !activeHighlights.length ? '' : activeHighlights.length > 0 ? String(activeHighlights[0]) : '...';
// update link in current selection
@@ -157,13 +144,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
const tr = updateBullets(state.tr.setNodeMarkup(state.selection.from, node.type, attrs), state.schema);
dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(state.selection.from))));
} else if (dontToggle) {
- toggleMark(mark.type, mark.attrs)(state, (tx: any) => {
- const { from, $from, to, empty } = tx.selection;
- if (!tx.doc.rangeHasMark(from, to, mark.type)) {
- // hack -- should have just set the mark in the first place
- toggleMark(mark.type, mark.attrs)({ tr: tx, doc: tx.doc, selection: tx.selection, storedMarks: tx.storedMarks }, dispatch);
- } else dispatch(tx);
- });
+ const tr = state.tr.addMark(state.selection.from, state.selection.to, mark);
+ dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(state.selection.from), tr.doc.resolve(state.selection.to)))); // bcz: need to redo the selection because ctrl-a selections disappear otherwise
} else {
toggleMark(mark.type, mark.attrs)(state, dispatch);
}
@@ -172,7 +154,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
// finds font sizes and families in selection
getActiveAlignment() {
- if (this.view && this.TextView.props.isSelected(true)) {
+ if (this.view && this.TextView?.props.isSelected(true)) {
const path = (this.view.state.selection.$from as any).path;
for (let i = path.length - 3; i < path.length && i >= 0; i -= 3) {
if (path[i]?.type === this.view.state.schema.nodes.paragraph || path[i]?.type === this.view.state.schema.nodes.heading) {
@@ -185,7 +167,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
// finds font sizes and families in selection
getActiveListStyle() {
- if (this.view && this.TextView.props.isSelected(true)) {
+ if (this.view && this.TextView?.props.isSelected(true)) {
const path = (this.view.state.selection.$from as any).path;
for (let i = 0; i < path.length; i += 3) {
if (path[i].type === this.view.state.schema.nodes.ordered_list) {
@@ -201,17 +183,16 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
// finds font sizes and families in selection
getActiveFontStylesOnSelection() {
- if (!this.view) return { activeFamilies: [], activeSizes: [], activeColors: [], activeHighlights: [] };
-
- const activeFamilies: string[] = [];
- const activeSizes: string[] = [];
- const activeColors: string[] = [];
- const activeHighlights: string[] = [];
- if (this.TextView.props.isSelected(true)) {
+ const activeFamilies = new Set<string>();
+ const activeSizes = new Set<string>();
+ const activeColors = new Set<string>();
+ const activeHighlights = new Set<string>();
+ if (this.view && this.TextView?.props.isSelected(true)) {
const state = this.view.state;
const pos = this.view.state.selection.$from;
const marks: Mark[] = [...(state.storedMarks ?? [])];
- if (state.selection.empty) {
+ if (state.storedMarks !== null) {
+ } else if (state.selection.empty) {
const ref_node = this.reference_node(pos);
marks.push(...(ref_node !== this.view.state.doc && ref_node?.isText ? Array.from(ref_node.marks) : []));
} else {
@@ -220,13 +201,13 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
});
}
marks.forEach(m => {
- m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family);
- m.type === state.schema.marks.pFontColor && activeColors.push(m.attrs.color);
- m.type === state.schema.marks.pFontSize && activeSizes.push(m.attrs.fontSize);
- m.type === state.schema.marks.marker && activeHighlights.push(String(m.attrs.highlight));
+ m.type === state.schema.marks.pFontFamily && activeFamilies.add(m.attrs.family);
+ m.type === state.schema.marks.pFontColor && activeColors.add(m.attrs.color);
+ m.type === state.schema.marks.pFontSize && activeSizes.add(m.attrs.fontSize);
+ m.type === state.schema.marks.marker && activeHighlights.add(String(m.attrs.highlight));
});
}
- return { activeFamilies, activeSizes, activeColors, activeHighlights };
+ return { activeFamilies: Array.from(activeFamilies), activeSizes: Array.from(activeSizes), activeColors: Array.from(activeColors), activeHighlights: Array.from(activeHighlights) };
}
getMarksInSelection(state: EditorState) {
@@ -239,7 +220,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
//finds all active marks on selection in given group
getActiveMarksOnSelection() {
let activeMarks: MarkType[] = [];
- if (!this.view || !this.TextView.props.isSelected(true)) return activeMarks;
+ if (!this.view || !this.TextView?.props.isSelected(true)) return activeMarks;
const markGroup = [schema.marks.noAutoLinkAnchor, schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript];
if (this.view.state.storedMarks) return this.view.state.storedMarks.map(mark => mark.type);
@@ -292,28 +273,15 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this._superscriptActive = false;
activeMarks.forEach(mark => {
+ // prettier-ignore
switch (mark.name) {
- case 'noAutoLinkAnchor':
- this._noLinkActive = true;
- break;
- case 'strong':
- this._boldActive = true;
- break;
- case 'em':
- this._italicsActive = true;
- break;
- case 'underline':
- this._underlineActive = true;
- break;
- case 'strikethrough':
- this._strikethroughActive = true;
- break;
- case 'subscript':
- this._subscriptActive = true;
- break;
- case 'superscript':
- this._superscriptActive = true;
- break;
+ case 'noAutoLinkAnchor': this._noLinkActive = true; break;
+ case 'strong': this._boldActive = true; break;
+ case 'em': this._italicsActive = true; break;
+ case 'underline': this._underlineActive = true; break;
+ case 'strikethrough': this._strikethroughActive = true; break;
+ case 'subscript': this._subscriptActive = true; break;
+ case 'superscript': this._superscriptActive = true; break;
}
});
}
@@ -355,14 +323,13 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
if (this.view.state.selection.from === 1 && this.view.state.selection.empty && (!this.view.state.doc.nodeAt(1) || !this.view.state.doc.nodeAt(1)?.marks.some(m => m.type.name === fontSize))) {
this.TextView.dataDoc.fontSize = fontSize;
this.view.focus();
- this.updateMenu(this.view, undefined, this.props);
} else {
const fmark = this.view.state.schema.marks.pFontSize.create({ fontSize });
this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true);
this.view.focus();
- this.updateMenu(this.view, undefined, this.props);
}
- }
+ } else Doc.UserDoc()._fontSize = fontSize;
+ this.updateMenu(this.view, undefined, this.props);
};
setFontFamily = (family: string) => {
@@ -370,8 +337,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
const fmark = this.view.state.schema.marks.pFontFamily.create({ family: family });
this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true);
this.view.focus();
- this.updateMenu(this.view, undefined, this.props);
- }
+ } else Doc.UserDoc()._fontFamily = family;
+ this.updateMenu(this.view, undefined, this.props);
};
setHighlight(color: String, view: EditorView, dispatch: any) {
@@ -381,20 +348,20 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this.setMark(highlightMark, view.state, dispatch, false);
}
- setColor(color: String, view: EditorView, dispatch: any) {
+ setColor(color: string) {
if (this.view) {
- const colorMark = view.state.schema.mark(view.state.schema.marks.pFontColor, { color });
+ const colorMark = this.view.state.schema.mark(this.view.state.schema.marks.pFontColor, { color });
this.setMark(colorMark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(colorMark)), true);
- view.focus();
- this.updateMenu(this.view, undefined, this.props);
- }
+ this.view.focus();
+ } else Doc.UserDoc().fontColor = color;
+ this.updateMenu(this.view, undefined, this.props);
}
// TODO: remove doesn't work
// 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;
@@ -443,7 +410,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
}
align = (view: EditorView, dispatch: any, alignment: 'left' | 'right' | 'center') => {
- if (this.TextView.props.isSelected(true)) {
+ if (this.TextView?.props.isSelected(true)) {
var tr = view.state.tr;
view.state.doc.nodesBetween(view.state.selection.from, view.state.selection.to, (node, pos, parent, index) => {
if ([schema.nodes.paragraph, schema.nodes.heading].includes(node.type)) {
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 1916b94bf..2eb62c38d 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -80,7 +80,7 @@ export class RichTextRules {
textDoc.inlineTextCount = numInlines + 1;
const inlineFieldKey = 'inline' + numInlines; // which field on the text document this annotation will write to
const inlineLayoutKey = 'layout_' + inlineFieldKey; // the field holding the layout string that will render the inline annotation
- const textDocInline = Docs.Create.TextDocument('', { _layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: '9px', title: 'inline comment' });
+ const textDocInline = Docs.Create.TextDocument('', { _layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _fitWidth: true, _autoHeight: true, _fontSize: '9px', title: 'inline comment' });
textDocInline.title = inlineFieldKey; // give the annotation its own title
textDocInline['title-custom'] = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
@@ -343,8 +343,14 @@ export class RichTextRules {
const node = (state.doc.resolve(start) as any).nodeAfter;
if (node?.marks.findIndex((m: any) => m.type === schema.marks.user_tag) !== -1) return state.tr.removeMark(start, end, schema.marks.user_tag);
-
- return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: tag, modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
+ if (node?.marks.findIndex((m: any) => m.type === schema.marks.user_mark) !== -1) {
+ }
+ return node
+ ? state.tr
+ .removeMark(start, end, schema.marks.user_mark)
+ .addMark(start, end, schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }))
+ .addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: tag, modified: Math.round(Date.now() / 1000 / 60) }))
+ : state.tr;
}),
new InputRule(new RegExp(/%\(/), (state, match, start, end) => {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index c95ece3da..82b8a8f90 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1,16 +1,18 @@
import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { ColorState, SketchPicker } from 'react-color';
import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
import { Doc, DocListCast, DocListCastAsync, FieldResult } from '../../../../fields/Doc';
+import { Copy } from '../../../../fields/FieldSymbols';
import { InkTool } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
+import { ObjectField } from '../../../../fields/ObjectField';
import { listSpec } from '../../../../fields/Schema';
import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents } from '../../../../Utils';
+import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents, StopEvent } from '../../../../Utils';
import { Docs } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { DocumentManager } from '../../../util/DocumentManager';
@@ -19,7 +21,7 @@ import { SelectionManager } from '../../../util/SelectionManager';
import { SettingsManager } from '../../../util/SettingsManager';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
import { CollectionDockingView } from '../../collections/CollectionDockingView';
-import { MarqueeViewBounds } from '../../collections/collectionFreeForm';
+import { CollectionFreeFormView, MarqueeViewBounds } from '../../collections/collectionFreeForm';
import { CollectionView } from '../../collections/CollectionView';
import { TabDocView } from '../../collections/TabDocView';
import { ViewBoxBaseComponent } from '../../DocComponent';
@@ -32,7 +34,7 @@ import { PresEffect, PresMovement, PresStatus } from './PresEnums';
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
@@ -66,52 +68,51 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
delay: presDoc.presTransition,
// when: this.layoutDoc === PresBox.Instance.childDocs[PresBox.Instance.itemIndex]?.presentationTargetDoc,
};
+ //prettier-ignore
switch (presDoc.presEffect) {
- case PresEffect.Zoom:
- return <Zoom {...effectProps}>{renderDoc}</Zoom>;
- case PresEffect.Fade:
- return <Fade {...effectProps}>{renderDoc}</Fade>;
- case PresEffect.Flip:
- return <Flip {...effectProps}>{renderDoc}</Flip>;
- case PresEffect.Rotate:
- return <Rotate {...effectProps}>{renderDoc}</Rotate>;
- case PresEffect.Bounce:
- return <Bounce {...effectProps}>{renderDoc}</Bounce>;
- case PresEffect.Roll:
- return <Roll {...effectProps}>{renderDoc}</Roll>;
- case PresEffect.Lightspeed:
- return <LightSpeed {...effectProps}>{renderDoc}</LightSpeed>;
- case PresEffect.None:
- default:
- return renderDoc;
+ default:
+ case PresEffect.None: return renderDoc;
+ case PresEffect.Zoom: return <Zoom {...effectProps}>{renderDoc}</Zoom>;
+ case PresEffect.Fade: return <Fade {...effectProps}>{renderDoc}</Fade>;
+ case PresEffect.Flip: return <Flip {...effectProps}>{renderDoc}</Flip>;
+ case PresEffect.Rotate: return <Rotate {...effectProps}>{renderDoc}</Rotate>;
+ case PresEffect.Bounce: return <Bounce {...effectProps}>{renderDoc}</Bounce>;
+ case PresEffect.Roll: return <Roll {...effectProps}>{renderDoc}</Roll>;
+ case PresEffect.Lightspeed: return <LightSpeed {...effectProps}>{renderDoc}</LightSpeed>;
}
}
public static EffectsProvider(layoutDoc: Doc, renderDoc: any) {
return PresBox.Instance && layoutDoc === PresBox.Instance.childDocs[PresBox.Instance.itemIndex]?.presentationTargetDoc ? PresBox.renderEffectsDoc(renderDoc, layoutDoc, PresBox.Instance.childDocs[PresBox.Instance.itemIndex]) : renderDoc;
}
+ private _disposers: { [name: string]: IReactionDisposer } = {};
+ public selectedArray = new ObservableSet<Doc>();
+
+ constructor(props: any) {
+ super(props);
+ if ((Doc.ActivePresentation = this.rootDoc)) runInAction(() => (PresBox.Instance = this));
+ }
+
@observable public static Instance: PresBox;
+ @observable static startMarquee: boolean = false; // onclick "+ new slide" in presentation mode, set as true, then when marquee selection finish, onPointerUp automatically triggers PinWithView
@observable _isChildActive = false;
@observable _moveOnFromAudio: boolean = true;
@observable _presTimer!: NodeJS.Timeout;
- @observable _presKeyEventsActive: boolean = false;
- @observable _selectedArray: ObservableMap = new ObservableMap<Doc, any>();
@observable _eleArray: HTMLElement[] = [];
@observable _dragArray: HTMLElement[] = [];
@observable _pathBoolean: boolean = false;
@observable _expandBoolean: boolean = false;
-
- private _disposers: { [name: string]: IReactionDisposer } = {};
-
- @observable static startMarquee: boolean = false; // onclick "+ new slide" in presentation mode, set as true, then when marquee selection finish, onPointerUp automatically triggers PinWithView
- @observable private transitionTools: boolean = false;
- @observable private newDocumentTools: boolean = false;
- @observable private progressivizeTools: boolean = false;
- @observable private openMovementDropdown: boolean = false;
- @observable private openEffectDropdown: boolean = false;
- @observable private presentTools: boolean = false;
+ @observable _transitionTools: boolean = false;
+ @observable _newDocumentTools: boolean = false;
+ @observable _progressivizeTools: boolean = false;
+ @observable _openMovementDropdown: boolean = false;
+ @observable _openEffectDropdown: boolean = false;
+ @observable _presentTools: boolean = false;
+ @observable _treeViewMap: Map<Doc, number> = new Map();
+ @observable _presKeyEvents: boolean = false;
+ @observable _forceKeyEvents: boolean = false;
@computed get isTreeOrStack() {
return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(StrCast(this.layoutDoc._viewType) as any);
}
@@ -124,15 +125,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get childDocs() {
return DocListCast(this.rootDoc[this.presFieldKey]);
}
- @observable _treeViewMap: Map<Doc, number> = new Map();
-
@computed get tagDocs() {
- const tagDocs: Doc[] = [];
- for (const doc of this.childDocs) {
- const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
- tagDocs.push(tagDoc);
- }
- return tagDocs;
+ return this.childDocs.map(doc => Cast(doc.presentationTargetDoc, Doc, null));
}
@computed get itemIndex() {
return NumCast(this.rootDoc._itemIndex);
@@ -143,42 +137,33 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get targetDoc() {
return Cast(this.activeItem?.presentationTargetDoc, Doc, null);
}
- @computed get scrollable(): boolean {
+ @computed get scrollable() {
if (this.targetDoc.type === DocumentType.PDF || this.targetDoc.type === DocumentType.WEB || this.targetDoc.type === DocumentType.RTF || this.targetDoc._viewType === CollectionViewType.Stacking) return true;
- else return false;
+ return false;
}
- @computed get panable(): boolean {
+ @computed get panable() {
if ((this.targetDoc.type === DocumentType.COL && this.targetDoc._viewType === CollectionViewType.Freeform) || this.targetDoc.type === DocumentType.IMG) return true;
- else return false;
- }
- constructor(props: any) {
- super(props);
- if ((Doc.ActivePresentation = this.rootDoc)) runInAction(() => (PresBox.Instance = this));
- this.props.Document.presentationFieldKey = this.fieldKey; // provide info to the presElement script so that it can look up rendering information about the presBox
+ return false;
}
@computed get selectedDocumentView() {
if (SelectionManager.Views().length) return SelectionManager.Views()[0];
- if (this._selectedArray.size) return DocumentManager.Instance.getDocumentView(this.rootDoc);
+ if (this.selectedArray.size) return DocumentManager.Instance.getDocumentView(this.rootDoc);
}
- @computed get isPres(): boolean {
- document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
- if (this.selectedDoc?.type === DocumentType.PRES) {
- document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
- document.addEventListener('keydown', PresBox.keyEventsWrapper, true);
- return true;
- }
- return false;
+ @computed get isPres() {
+ return this.selectedDoc === this.rootDoc;
}
@computed get selectedDoc() {
return this.selectedDocumentView?.rootDoc;
}
+ clearSelectedArray = () => this.selectedArray.clear();
+ addToSelectedArray = (doc: Doc) => this.selectedArray.add(doc);
+ removeFromSelectedArray = (doc: Doc) => this.selectedArray.delete(doc);
_unmounting = false;
@action
componentWillUnmount() {
this._unmounting = true;
document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
- this._presKeyEventsActive = false;
this.resetPresentation();
// Turn of progressivize editors
this.turnOffEdit(true);
@@ -187,6 +172,27 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@action
componentDidMount() {
+ this._disposers.keyboard = reaction(
+ () => this.selectedDoc,
+ selected => {
+ document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
+ (this._presKeyEvents = selected === this.rootDoc) && document.addEventListener('keydown', PresBox.keyEventsWrapper, true);
+ },
+ { fireImmediately: true }
+ );
+ this._disposers.forcekeyboard = reaction(
+ () => this._forceKeyEvents,
+ force => {
+ if (force) {
+ document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
+ document.addEventListener('keydown', PresBox.keyEventsWrapper, true);
+ this._presKeyEvents = true;
+ }
+ this._forceKeyEvents = false;
+ },
+ { fireImmediately: true }
+ );
+ this.props.setContentView?.(this);
this._unmounting = false;
this.rootDoc._forceRenderEngine = 'timeline';
this.layoutDoc.presStatus = PresStatus.Edit;
@@ -204,11 +210,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@action
updateCurrentPresentation = (pres?: Doc) => {
- if (pres) Doc.ActivePresentation = pres;
- else Doc.ActivePresentation = this.rootDoc;
- document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
- document.addEventListener('keydown', PresBox.keyEventsWrapper, true);
- this._presKeyEventsActive = true;
+ Doc.ActivePresentation = pres ?? this.rootDoc;
PresBox.Instance = this;
};
@@ -312,12 +314,25 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
//The function that is called when a document is clicked or reached through next or back.
//it'll also execute the necessary actions if presentation is playing.
+ @undoBatch
public gotoDocument = action((index: number, from?: Doc, group?: boolean) => {
Doc.UnBrushAllDocs();
+
if (index >= 0 && index < this.childDocs.length) {
this.rootDoc._itemIndex = index;
const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
+ if (activeItem.presActiveFrame !== undefined) {
+ const transTime = NumCast(activeItem.presTransition, 500);
+ const context = DocCast(DocCast(activeItem.presentationTargetDoc).context);
+ if (context) {
+ const contextView = DocumentManager.Instance.getFirstDocumentView(context);
+ if (contextView?.ComponentView) {
+ CollectionFreeFormDocumentView.gotoKeyframe((contextView.ComponentView as CollectionFreeFormView).childDocs.slice(), transTime);
+ context._currentFrame = NumCast(activeItem.presActiveFrame);
+ }
+ }
+ }
if (from?.mediaStopTriggerList && this.layoutDoc.presStatus !== PresStatus.Edit) {
DocListCast(from.mediaStopTriggerList).forEach(this.stopTempMedia);
}
@@ -330,62 +345,97 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
if (targetDoc) {
Doc.linkFollowHighlight(targetDoc.annotationOn instanceof Doc ? [targetDoc, targetDoc.annotationOn] : targetDoc);
- targetDoc &&
- runInAction(() => {
- if (activeItem.presMovement === PresMovement.Jump) targetDoc.focusSpeed = 0;
- else targetDoc.focusSpeed = activeItem.presTransition ? activeItem.presTransition : 500;
- });
- setTimeout(() => (targetDoc.focusSpeed = 500), this.activeItem.presTransition ? NumCast(this.activeItem.presTransition) + 10 : 510);
+ targetDoc && runInAction(() => (targetDoc.focusSpeed = activeItem.presMovement === PresMovement.Jump ? 0 : NumCast(activeItem.presTransition, 500)));
+ setTimeout(() => (targetDoc.focusSpeed = undefined), NumCast(targetDoc.focusSpeed) + 10);
}
if (targetDoc?.lastFrame !== undefined) {
targetDoc._currentFrame = 0;
}
- if (!group) this._selectedArray.clear();
- this.childDocs[index] && this._selectedArray.set(this.childDocs[index], undefined); //Update selected array
+ if (!group) this.clearSelectedArray();
+ this.childDocs[index] && this.addToSelectedArray(this.childDocs[index]); //Update selected array
this.navigateToElement(this.childDocs[index]); //Handles movement to element only when presTrail is list
this.onHideDocument(); //Handles hide after/before
}
});
+ static pinDataTypes(target: Doc) {
+ const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(target.type as any) || target._viewType === CollectionViewType.Stacking;
+ const pannable = [DocumentType.IMG].includes(target.type as any) || (target.type === DocumentType.COL && target._viewType === CollectionViewType.Freeform);
+ const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(target.type as any);
+ const clippable = [DocumentType.COMPARISON].includes(target.type as any);
+ const dataview = [DocumentType.INK].includes(target.type as any) && target.activeFrame === undefined;
+ const textview = [DocumentType.RTF].includes(target.type as any) && target.activeFrame === undefined;
+ return { scrollable, pannable, temporal, clippable, dataview, textview };
+ }
- _navTimer!: NodeJS.Timeout;
- navigateToView = (targetDoc: Doc, activeItem: Doc) => {
- clearTimeout(this._navTimer);
- const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
- if (bestTarget) console.log(bestTarget.title, bestTarget.type);
- else console.log('no best target');
- if (bestTarget) this._navTimer = PresBox.navigateToDoc(bestTarget, activeItem, false);
- };
-
- // navigates to the bestTarget document by making sure it is on screen,
- // then it applies the view specs stored in activeItem to
@action
- static navigateToDoc(bestTarget: Doc, activeItem: Doc, jumpToDoc: boolean) {
- if (bestTarget.type === DocumentType.PDF || bestTarget.type === DocumentType.WEB || bestTarget.type === DocumentType.RTF || bestTarget._viewType === CollectionViewType.Stacking) {
- bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
- bestTarget._scrollTop = activeItem.presPinViewScroll;
- } else if (bestTarget.type === DocumentType.COMPARISON) {
- bestTarget._clipWidth = activeItem.presPinClipWidth;
- } else if ([DocumentType.AUDIO, DocumentType.VID].includes(bestTarget.type as any)) {
- bestTarget._currentTimecode = activeItem.presStartTime;
- } else {
- const contentBounds = Cast(activeItem.contentBounds, listSpec('number'));
- bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
+ static restoreTargetDocView(bestTarget: Doc, activeItem: Doc, jumpToDoc: boolean) {
+ const transTime = NumCast(activeItem.presTransition, 500);
+ const presTransitionTime = `all ${transTime}ms`;
+ const { scrollable, pannable, temporal, clippable, dataview, textview } = this.pinDataTypes(bestTarget);
+ bestTarget._viewTransition = presTransitionTime;
+ if (clippable) bestTarget._clipWidth = activeItem.presPinClipWidth;
+ if (temporal) bestTarget._currentTimecode = activeItem.presStartTime;
+ if (scrollable) bestTarget._scrollTop = activeItem.presPinViewScroll;
+ if (dataview) Doc.GetProto(bestTarget).data = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
+ if (textview) Doc.GetProto(bestTarget).text = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
+ if (pannable) {
+ bestTarget._panX = activeItem.presPinViewX;
+ bestTarget._panY = activeItem.presPinViewY;
+ bestTarget._viewScale = activeItem.presPinViewScale;
+ const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
if (contentBounds) {
- bestTarget._panX = (contentBounds[0] + contentBounds[2]) / 2;
- bestTarget._panY = (contentBounds[1] + contentBounds[3]) / 2;
const dv = DocumentManager.Instance.getDocumentView(bestTarget);
- if (dv) {
- bestTarget._viewScale = Math.min(dv.props.PanelHeight() / (contentBounds[3] - contentBounds[1]), dv.props.PanelWidth() / (contentBounds[2] - contentBounds[0]));
+ dv && (bestTarget._viewScale = Math.min(dv.props.PanelHeight() / (contentBounds[3] - contentBounds[1]), dv.props.PanelWidth() / (contentBounds[2] - contentBounds[0])));
+ }
+ }
+ return setTimeout(() => (bestTarget._viewTransition = undefined), transTime + 10);
+ }
+
+ /// copies values from the targetDoc (which is the prototype of the pinDoc) to
+ /// reserved fields on the pinDoc so that those values can be restored to the
+ /// target doc when navigating to it.
+ @action
+ static pinDocView(pinDoc: Doc, pinProps: PinProps | undefined, targetDoc: Doc) {
+ if (pinProps?.pinWithView) {
+ // If pinWithView option set then update scale and x / y props of slide
+ const bounds = pinProps.pinWithView.bounds;
+ pinDoc.presPinView = true;
+ pinDoc.presPinViewX = bounds.left + bounds.width / 2;
+ pinDoc.presPinViewY = bounds.top + bounds.height / 2;
+ pinDoc.presPinViewScale = pinProps.pinWithView.scale;
+ pinDoc.presPinViewBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
+ }
+ if (pinProps?.pinDocView) {
+ const { scrollable, pannable, temporal, clippable, dataview, textview } = this.pinDataTypes(pinDoc);
+ pinDoc.presPinView = (pinProps?.pinWithView ? true : false) || scrollable || temporal || pannable || clippable || dataview || textview || pinProps.activeFrame !== undefined;
+ pinDoc.presX = NumCast(targetDoc.x);
+ pinDoc.presY = NumCast(targetDoc.y);
+ pinDoc.presRot = NumCast(targetDoc.jitterRotation);
+ pinDoc.presWidth = NumCast(targetDoc.width);
+ pinDoc.presHeight = NumCast(targetDoc.height);
+
+ if (scrollable) pinDoc.presPinViewScroll = pinDoc._scrollTop;
+ if (clippable) pinDoc.presPinClipWidth = pinDoc._clipWidth;
+ if (temporal) pinDoc.presEndTime = NumCast((pinDoc.presStartTime = pinDoc._currentTimecode)) + 0.1;
+ if (textview) pinDoc.presData = targetDoc.text instanceof ObjectField ? targetDoc.text[Copy]() : targetDoc.text;
+ if (dataview) pinDoc.presData = targetDoc.data instanceof ObjectField ? targetDoc.data[Copy]() : targetDoc.data;
+ if (pannable) {
+ const panX = NumCast(pinDoc._panX);
+ const panY = NumCast(pinDoc._panY);
+ const pw = NumCast(pinProps.panelWidth);
+ const ph = NumCast(pinProps.panelHeight);
+ const ps = NumCast(pinDoc._viewScale, 1);
+ if (pw && ph && ps) {
+ pinDoc.presPinViewBounds = new List<number>([panX - pw / 2 / ps, panY - ph / 2 / ps, panX + pw / 2 / ps, panY + ph / 2 / ps]);
}
- } else {
- bestTarget._panX = activeItem.presPinViewX;
- bestTarget._panY = activeItem.presPinViewY;
- bestTarget._viewScale = activeItem.presPinViewScale;
+ pinDoc.presPinViewX = panX;
+ pinDoc.presPinViewY = panY;
+ pinDoc.presPinViewScale = ps;
}
}
- return setTimeout(() => (bestTarget._viewTransition = undefined), activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 510);
}
+ _navTimer!: NodeJS.Timeout;
/**
* This method makes sure that cursor navigates to the element that
* has the option open and last in the group.
@@ -401,7 +451,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) {
@@ -411,7 +461,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
this.layoutDoc.presCollection = srcContext;
}
const presStatus = this.rootDoc.presStatus;
- const selViewCache = Array.from(this._selectedArray.keys());
+ const selViewCache = Array.from(this.selectedArray);
const dragViewCache = Array.from(this._dragArray);
const eleViewCache = Array.from(this._eleArray);
const self = this;
@@ -419,8 +469,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const presDocView = DocumentManager.Instance.getDocumentView(self.rootDoc);
if (presDocView) SelectionManager.SelectView(presDocView, false);
self.rootDoc.presStatus = presStatus;
- self._selectedArray.clear();
- selViewCache.forEach(doc => self._selectedArray.set(doc, undefined));
+ self.clearSelectedArray();
+ selViewCache.forEach(doc => self.addToSelectedArray(doc));
self._dragArray.splice(0, self._dragArray.length, ...dragViewCache);
self._eleArray.splice(0, self._eleArray.length, ...eleViewCache);
});
@@ -435,25 +485,32 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
finished?.();
}
};
+ if (activeItem.presPinView && DocCast(targetDoc.context)?._currentFrame === undefined) {
+ const transTime = NumCast(activeItem.presTransition, 500);
+ const presTransitionTime = `all ${transTime}ms`;
+ targetDoc._dataTransition = presTransitionTime;
+ targetDoc.x = NumCast(activeItem.presX, NumCast(targetDoc.x));
+ targetDoc.y = NumCast(activeItem.presY, NumCast(targetDoc.y));
+ targetDoc.jitterRotation = NumCast(activeItem.presRot, NumCast(targetDoc.jitterRotation));
+ targetDoc.width = NumCast(activeItem.presWidth, NumCast(targetDoc.width));
+ targetDoc.height = NumCast(activeItem.presHeight, NumCast(targetDoc.height));
+ setTimeout(() => (targetDoc._dataTransition = undefined), transTime + 10);
+ }
// If openDocument is selected then it should open the document for the user
if (activeItem.openDocument) {
- LightboxView.SetLightboxDoc(targetDoc);
- // openInTab(targetDoc);
- } else if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
- LightboxView.SetLightboxDoc(undefined);
- await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
- } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) {
+ LightboxView.SetLightboxDoc(targetDoc); // openInTab(targetDoc);
+ } else if (targetDoc && curDoc.presMovement !== PresMovement.None && targetDoc) {
LightboxView.SetLightboxDoc(undefined);
- //awaiting jump so that new scale can be found, since jumping is async
- await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right
+ const zooming = curDoc.presMovement !== PresMovement.Pan;
+ DocumentManager.Instance.jumpToDocument(targetDoc, zooming, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom));
}
// After navigating to the document, if it is added as a presPinView then it will
// adjust the pan and scale to that of the pinView when it was added.
if (activeItem.presPinView) {
- console.log(targetDoc.title);
- console.log('presPinView in PresBox.tsx:420');
// if targetDoc is not displayed but one of its aliases is, then we need to modify that alias, not the original target
- this.navigateToView(targetDoc, activeItem);
+ clearTimeout(this._navTimer);
+ const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
+ if (bestTarget) this._navTimer = PresBox.restoreTargetDocView(bestTarget, activeItem, false);
}
};
@@ -511,26 +568,32 @@ 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) {
tagDoc.opacity = 1;
} else {
- if (itemIndexes.length > 1 && curDoc.presHideBefore && curInd !== 0) {
- } else if (curDoc.presHideBefore) {
- if (index > this.itemIndex) {
- tagDoc.opacity = 0;
- } else if (!curDoc.presHideAfter) {
+ if (curDoc.presHideBefore) {
+ if (itemIndexes.length > 1 && curInd !== 0) {
tagDoc.opacity = 1;
+ } else {
+ if (index > this.itemIndex) {
+ tagDoc.opacity = 0;
+ } else if (index === this.itemIndex || !curDoc.presHideAfter) {
+ tagDoc.opacity = 1;
+ }
}
}
- if (itemIndexes.length > 1 && curDoc.presHideAfter && curInd !== itemIndexes.length - 1) {
- } else if (curDoc.presHideAfter) {
- if (index < this.itemIndex) {
- tagDoc.opacity = 0;
- } else if (!curDoc.presHideBefore) {
+ if (curDoc.presHideAfter) {
+ if (itemIndexes.length > 1 && curInd !== itemIndexes.length - 1) {
tagDoc.opacity = 1;
+ } else {
+ if (index < this.itemIndex) {
+ tagDoc.opacity = 0;
+ } else if (index === this.itemIndex || !curDoc.presHideBefore) {
+ tagDoc.opacity = 1;
+ }
}
}
}
@@ -707,27 +770,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
});
- setMovementName = action((movement: any, activeItem: Doc): string => {
- let output: string = 'none';
- switch (movement) {
- case PresMovement.Zoom:
- output = 'Pan & Zoom';
- break; //Pan and zoom
- case PresMovement.Pan:
- output = 'Pan';
- break; //Pan
- case PresMovement.Jump:
- output = 'Jump cut';
- break; //Jump Cut
- case PresMovement.None:
- output = 'None';
- break; //None
- default:
- output = 'Zoom';
- activeItem.presMovement = 'zoom';
- break; //default set as zoom
+ movementName = action((activeItem: Doc) => {
+ if (![PresMovement.Zoom, PresMovement.Pan, PresMovement.Jump, PresMovement.None].includes(StrCast(activeItem.presMovement) as any)) {
+ activeItem.presMovement = 'zoom';
}
- return output;
+ return StrCast(activeItem.presMovement);
});
whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isChildActive = isActive)));
@@ -773,16 +820,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
/**
* For sorting the array so that the order is maintained when it is dropped.
*/
- @action
- sortArray = (): Doc[] => {
- return this.childDocs.filter(doc => this._selectedArray.has(doc));
- };
+ sortArray = () => this.childDocs.filter(doc => this.selectedArray.has(doc));
/**
* Method to get the list of selected items in the order in which they have been selected
*/
@computed get listOfSelected() {
- const list = Array.from(this._selectedArray.keys()).map((doc: Doc, index: any) => {
+ return Array.from(this.selectedArray).map((doc: Doc, index: any) => {
const curDoc = Cast(doc, Doc, null);
const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
if (curDoc && curDoc === this.activeItem)
@@ -806,7 +850,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>
);
});
- return list;
}
@action
@@ -818,54 +861,34 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
//Regular click
@action
selectElement = async (doc: Doc) => {
- const context = Cast(doc.context, Doc, null);
this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
- 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 (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(DocCast(doc.context)), 0);
+ else this.updateCurrentPresentation(DocCast(doc.context));
};
//Command click
@action
multiSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => {
- if (!this._selectedArray.has(doc)) {
- this._selectedArray.set(doc, undefined);
+ if (!this.selectedArray.has(doc)) {
+ this.addToSelectedArray(doc);
this._eleArray.push(ref);
this._dragArray.push(drag);
} else {
- this._selectedArray.delete(doc);
- this.removeFromArray(this._eleArray, doc);
- this.removeFromArray(this._dragArray, doc);
+ this.removeFromSelectedArray(doc);
+ this._eleArray.splice(this._eleArray.indexOf(ref));
+ this._dragArray.splice(this._dragArray.indexOf(drag));
}
this.selectPres();
};
- removeFromArray = (arr: any[], val: any) => {
- const index: number = arr.indexOf(val);
- const ret: any[] = arr.splice(index, 1);
- arr = ret;
- };
-
//Shift click
@action
shiftSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => {
- this._selectedArray.clear();
+ this.clearSelectedArray();
// const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
if (this.activeItem) {
for (let i = Math.min(this.itemIndex, this.childDocs.indexOf(doc)); i <= Math.max(this.itemIndex, this.childDocs.indexOf(doc)); i++) {
- this._selectedArray.set(this.childDocs[i], undefined);
+ this.addToSelectedArray(this.childDocs[i]);
this._eleArray.push(ref);
this._dragArray.push(drag);
}
@@ -876,8 +899,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
//regular click
@action
regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, selectPres = true) => {
- this._selectedArray.clear();
- this._selectedArray.set(doc, undefined);
+ this.clearSelectedArray();
+ this.addToSelectedArray(doc);
this._eleArray.splice(0, this._eleArray.length, ref);
this._dragArray.splice(0, this._dragArray.length, drag);
focus && this.selectElement(doc);
@@ -890,9 +913,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
else this.regularSelect(doc, ref, drag, focus);
};
- static keyEventsWrapper = (e: KeyboardEvent) => {
- PresBox.Instance.keyEvents(e);
- };
+ static keyEventsWrapper = (e: KeyboardEvent) => PresBox.Instance.keyEvents(e);
// Key for when the presentaiton is active
@action
@@ -906,10 +927,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (this.layoutDoc.presStatus === 'edit') {
undoBatch(
action(() => {
- for (const doc of Array.from(this._selectedArray.keys())) {
+ for (const doc of this.selectedArray) {
this.removeDocument(doc);
}
- this._selectedArray.clear();
+ this.clearSelectedArray();
this._eleArray.length = 0;
this._dragArray.length = 0;
})
@@ -921,7 +942,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
this.updateMinimize();
} else if (this.layoutDoc.presStatus === 'edit') {
- this._selectedArray.clear();
+ this.clearSelectedArray();
this._eleArray.length = this._dragArray.length = 0;
} else this.layoutDoc.presStatus = 'edit';
if (this._presTimer) clearTimeout(this._presTimer);
@@ -934,7 +955,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (e.shiftKey && this.itemIndex < this.childDocs.length - 1) {
// TODO: update to work properly
this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) + 1;
- this._selectedArray.set(this.childDocs[this.rootDoc._itemIndex], undefined);
+ this.addToSelectedArray(this.childDocs[this.rootDoc._itemIndex]);
} else {
this.next();
if (this._presTimer) {
@@ -951,7 +972,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (e.shiftKey && this.itemIndex !== 0) {
// TODO: update to work properly
this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) - 1;
- this._selectedArray.set(this.childDocs[this.rootDoc._itemIndex], undefined);
+ this.addToSelectedArray(this.childDocs[this.rootDoc._itemIndex]);
} else {
this.back();
if (this._presTimer) {
@@ -969,8 +990,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
break;
case 'a':
if ((e.metaKey || e.altKey) && this.layoutDoc.presStatus === 'edit') {
- this._selectedArray.clear();
- this.childDocs.forEach(doc => this._selectedArray.set(doc, undefined));
+ this.clearSelectedArray();
+ this.childDocs.forEach(doc => this.addToSelectedArray(doc));
handled = true;
}
default:
@@ -993,13 +1014,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
};
- getAllIndexes = (arr: Doc[], val: Doc): number[] => {
- const indexes = [];
- for (let i = 0; i < arr.length; i++) {
- arr[i] === val && indexes.push(i);
- }
- return indexes;
- };
+ getAllIndexes = (arr: Doc[], val: Doc) => arr.map((doc, i) => (doc === val ? i : -1)).filter(i => i !== -1);
// Adds the index in the pres path graphically
@computed get order() {
@@ -1118,7 +1133,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (change) timeInMS += change;
if (timeInMS < 100) timeInMS = 100;
if (timeInMS > 10000) timeInMS = 10000;
- Array.from(this._selectedArray.keys()).forEach(doc => (doc.presTransition = timeInMS));
+ this.selectedArray.forEach(doc => (doc.presTransition = timeInMS));
};
// Converts seconds to ms and updates presTransition
@@ -1127,7 +1142,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (change) scale += change;
if (scale < 0.01) scale = 0.01;
if (scale > 1.5) scale = 1.5;
- Array.from(this._selectedArray.keys()).forEach(doc => (doc.presZoom = scale));
+ this.selectedArray.forEach(doc => (doc.presZoom = scale));
};
// Converts seconds to ms and updates presDuration
@@ -1136,114 +1151,43 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (change) timeInMS += change;
if (timeInMS < 100) timeInMS = 100;
if (timeInMS > 20000) timeInMS = 20000;
- Array.from(this._selectedArray.keys()).forEach(doc => (doc.presDuration = timeInMS));
+ this.selectedArray.forEach(doc => (doc.presDuration = timeInMS));
};
/**
* When the movement dropdown is changes
*/
@undoBatch
- updateMovement = action((movement: any, all?: boolean) => {
- const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys());
- array.forEach(doc => {
- switch (movement) {
- case PresMovement.Zoom: //Pan and zoom
- doc.presMovement = PresMovement.Zoom;
- break;
- case PresMovement.Pan: //Pan
- doc.presMovement = PresMovement.Pan;
- break;
- case PresMovement.Jump: //Jump Cut
- doc.presJump = true;
- doc.presMovement = PresMovement.Jump;
- break;
- case PresMovement.None:
- default:
- doc.presMovement = PresMovement.None;
- break;
- }
- });
- });
+ updateMovement = action((movement: PresMovement, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presMovement = movement)));
@undoBatch
@action
updateHideBefore = (activeItem: Doc) => {
activeItem.presHideBefore = !activeItem.presHideBefore;
- Array.from(this._selectedArray.keys()).forEach(doc => (doc.presHideBefore = activeItem.presHideBefore));
+ this.selectedArray.forEach(doc => (doc.presHideBefore = activeItem.presHideBefore));
};
@undoBatch
@action
updateHideAfter = (activeItem: Doc) => {
activeItem.presHideAfter = !activeItem.presHideAfter;
- Array.from(this._selectedArray.keys()).forEach(doc => (doc.presHideAfter = activeItem.presHideAfter));
+ this.selectedArray.forEach(doc => (doc.presHideAfter = activeItem.presHideAfter));
};
@undoBatch
@action
updateOpenDoc = (activeItem: Doc) => {
activeItem.openDocument = !activeItem.openDocument;
- Array.from(this._selectedArray.keys()).forEach(doc => {
- doc.openDocument = activeItem.openDocument;
- });
+ this.selectedArray.forEach(doc => (doc.openDocument = activeItem.openDocument));
};
@undoBatch
@action
- updateEffectDirection = (effect: any, all?: boolean) => {
- const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys());
- array.forEach(doc => {
- const tagDoc = doc; // Cast(doc.presentationTargetDoc, Doc, null);
- switch (effect) {
- case PresEffect.Left:
- tagDoc.presEffectDirection = PresEffect.Left;
- break;
- case PresEffect.Right:
- tagDoc.presEffectDirection = PresEffect.Right;
- break;
- case PresEffect.Top:
- tagDoc.presEffectDirection = PresEffect.Top;
- break;
- case PresEffect.Bottom:
- tagDoc.presEffectDirection = PresEffect.Bottom;
- break;
- case PresEffect.Center:
- default:
- tagDoc.presEffectDirection = PresEffect.Center;
- break;
- }
- });
- };
+ updateEffectDirection = (effect: PresEffect, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presEffectDirection = effect));
@undoBatch
@action
- updateEffect = (effect: any, all?: boolean) => {
- const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys());
- array.forEach(doc => {
- const tagDoc = doc; //Cast(doc.presentationTargetDoc, Doc, null);
- switch (effect) {
- case PresEffect.Bounce:
- tagDoc.presEffect = PresEffect.Bounce;
- break;
- case PresEffect.Fade:
- tagDoc.presEffect = PresEffect.Fade;
- break;
- case PresEffect.Flip:
- tagDoc.presEffect = PresEffect.Flip;
- break;
- case PresEffect.Roll:
- tagDoc.presEffect = PresEffect.Roll;
- break;
- case PresEffect.Rotate:
- tagDoc.presEffect = PresEffect.Rotate;
- break;
- case PresEffect.None:
- default:
- tagDoc.presEffect = PresEffect.None;
- break;
- }
- });
- };
+ updateEffect = (effect: PresEffect, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presEffect = effect));
_batch: UndoManager.Batch | undefined = undefined;
@@ -1252,6 +1196,46 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const targetDoc: Doc = this.targetDoc;
const isPresCollection: boolean = targetDoc === this.layoutDoc.presCollection;
const isPinWithView: boolean = BoolCast(activeItem.presPinView);
+ const presEffect = (effect: PresEffect) => (
+ <div className={`presBox-dropdownOption ${this.activeItem.presEffect === effect || (effect === PresEffect.None && !this.activeItem.presEffect) ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateEffect(effect)}>
+ {effect}
+ </div>
+ );
+ const presMovement = (movement: PresMovement) => (
+ <div className={`presBox-dropdownOption ${activeItem.presMovement === movement ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateMovement(movement)}>
+ {movement}
+ </div>
+ );
+ const presDirection = (diretion: PresEffect, icon: string, gridColumn: number, gridRow: number, opts: object) => {
+ const color = this.activeItem.presEffectDirection === diretion || (diretion === PresEffect.Center && !this.activeItem.presEffectDirection) ? Colors.LIGHT_BLUE : 'black';
+ return (
+ <Tooltip title={<div className="dash-tooltip">{diretion}</div>}>
+ <div
+ style={{ ...opts, border: diretion === PresEffect.Center ? `solid 2px ${color}` : undefined, borderRadius: '100%', cursor: 'pointer', gridColumn, gridRow, justifySelf: 'center', color }}
+ onClick={() => this.updateEffectDirection(diretion)}>
+ {icon ? <FontAwesomeIcon icon={icon as any} /> : null}
+ </div>
+ </Tooltip>
+ );
+ };
+ const inputter = (min: string, step: string, max: string, value: number, active: boolean, change: (val: string) => void) => {
+ return (
+ <input
+ type="range"
+ step={step}
+ min={min}
+ max={max}
+ value={value}
+ className={`toolbar-slider ${active ? '' : 'none'}`}
+ onPointerDown={() => (this._batch = UndoManager.StartBatch('pres slider'))}
+ onPointerUp={() => this._batch?.end()}
+ onChange={e => {
+ e.stopPropagation();
+ change(e.target.value);
+ }}
+ />
+ );
+ };
if (activeItem && targetDoc) {
const type = targetDoc.type;
const transitionSpeed = activeItem.presTransition ? NumCast(activeItem.presTransition) / 1000 : 0.5;
@@ -1262,13 +1246,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : 'Zoom';
return (
<div
- className={`presBox-ribbon ${this.transitionTools && this.layoutDoc.presStatus === PresStatus.Edit ? 'active' : ''}`}
- onPointerDown={e => e.stopPropagation()}
- onPointerUp={e => e.stopPropagation()}
+ className={`presBox-ribbon ${this._transitionTools && this.layoutDoc.presStatus === PresStatus.Edit ? 'active' : ''}`}
+ onPointerDown={StopEvent}
+ onPointerUp={StopEvent}
onClick={action(e => {
e.stopPropagation();
- this.openMovementDropdown = false;
- this.openEffectDropdown = false;
+ this._openMovementDropdown = false;
+ this._openEffectDropdown = false;
})}>
<div className="ribbon-box">
Movement
@@ -1281,24 +1265,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
className="presBox-dropdown"
onClick={action(e => {
e.stopPropagation();
- this.openMovementDropdown = !this.openMovementDropdown;
+ this._openMovementDropdown = !this._openMovementDropdown;
})}
- style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
- {this.setMovementName(activeItem.presMovement, activeItem)}
- <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this.openMovementDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
- <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={e => e.stopPropagation()} style={{ display: this.openMovementDropdown ? 'grid' : 'none' }}>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.None ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.None)}>
- None
- </div>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Zoom ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Zoom)}>
- Pan {'&'} Zoom
- </div>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Pan ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Pan)}>
- Pan
- </div>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Jump ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Jump)}>
- Jump cut
- </div>
+ style={{ borderBottomLeftRadius: this._openMovementDropdown ? 0 : 5, border: this._openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
+ {this.movementName(activeItem)}
+ <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openMovementDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
+ <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={StopEvent} style={{ display: this._openMovementDropdown ? 'grid' : 'none' }}>
+ {presMovement(PresMovement.None)}
+ {presMovement(PresMovement.Zoom)}
+ {presMovement(PresMovement.Pan)}
+ {presMovement(PresMovement.Jump)}
</div>
</div>
)}
@@ -1316,21 +1292,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>
</div>
</div>
- <input
- type="range"
- step="1"
- min="0"
- max="150"
- value={zoom}
- className={`toolbar-slider ${activeItem.presMovement === PresMovement.Zoom ? '' : 'none'}`}
- id="toolbar-slider"
- onPointerDown={() => (this._batch = UndoManager.StartBatch('presZoom'))}
- onPointerUp={() => this._batch?.end()}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
- e.stopPropagation();
- this.setZoom(e.target.value);
- }}
- />
+ {inputter('0', '1', '150', zoom, activeItem.presMovement === PresMovement.Zoom, this.setZoom)}
<div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
<div className="presBox-subheading">Movement Speed</div>
<div className="ribbon-property">
@@ -1345,21 +1307,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>
</div>
</div>
- <input
- type="range"
- step="0.1"
- min="0.1"
- max="10"
- value={transitionSpeed}
- className={`toolbar-slider ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? '' : 'none'}`}
- id="toolbar-slider"
- onPointerDown={() => (this._batch = UndoManager.StartBatch('presTransition'))}
- onPointerUp={() => this._batch?.end()}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
- e.stopPropagation();
- this.setTransitionTime(e.target.value);
- }}
- />
+ {inputter('0.1', '0.1', '10', transitionSpeed, [PresMovement.Pan, PresMovement.Zoom].includes(activeItem.presMovement as any), this.setTransitionTime)}
<div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? '' : 'none'}`}>
<div className="slider-text">Fast</div>
<div className="slider-text">Medium</div>
@@ -1370,35 +1318,20 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
Visibility {'&'} Duration
<div className="ribbon-doubleButton">
{isPresCollection ? null : (
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Hide before presented'}</div>
- </>
- }>
+ <Tooltip title={<div className="dash-tooltip">{'Hide before presented'}</div>}>
<div className={`ribbon-toggle ${activeItem.presHideBefore ? 'active' : ''}`} onClick={() => this.updateHideBefore(activeItem)}>
Hide before
</div>
</Tooltip>
)}
{isPresCollection ? null : (
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Hide after presented'}</div>
- </>
- }>
+ <Tooltip title={<div className="dash-tooltip">{'Hide after presented'}</div>}>
<div className={`ribbon-toggle ${activeItem.presHideAfter ? 'active' : ''}`} onClick={() => this.updateHideAfter(activeItem)}>
Hide after
</div>
</Tooltip>
)}
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Open in lightbox view'}</div>
- </>
- }>
+ <Tooltip title={<div className="dash-tooltip">{'Open in lightbox view'}</div>}>
<div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? Colors.LIGHT_BLUE : '' }} onClick={() => this.updateOpenDoc(activeItem)}>
Lightbox
</div>
@@ -1420,26 +1353,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>
</div>
</div>
- <input
- type="range"
- step="0.1"
- min="0.1"
- max="20"
- value={duration}
- style={{ display: targetDoc.type === DocumentType.AUDIO ? 'none' : 'block' }}
- className={'toolbar-slider'}
- id="duration-slider"
- onPointerDown={() => {
- this._batch = UndoManager.StartBatch('presDuration');
- }}
- onPointerUp={() => {
- if (this._batch) this._batch.end();
- }}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
- e.stopPropagation();
- this.setDurationTime(e.target.value);
- }}
- />
+ {inputter('0.1', '0.1', '20', duration, targetDoc.type !== DocumentType.AUDIO, this.setDurationTime)}
<div className={'slider-headers'} style={{ display: targetDoc.type === DocumentType.AUDIO ? 'none' : 'grid' }}>
<div className="slider-text">Short</div>
<div className="slider-text">Medium</div>
@@ -1455,98 +1369,30 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
className="presBox-dropdown"
onClick={action(e => {
e.stopPropagation();
- this.openEffectDropdown = !this.openEffectDropdown;
+ this._openEffectDropdown = !this._openEffectDropdown;
})}
- style={{ borderBottomLeftRadius: this.openEffectDropdown ? 0 : 5, border: this.openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
+ style={{ borderBottomLeftRadius: this._openEffectDropdown ? 0 : 5, border: this._openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
{effect.toString()}
- <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this.openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
- <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this.openEffectDropdown ? 'grid' : 'none' }} onPointerDown={e => e.stopPropagation()}>
- <div
- className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.None || !this.activeItem.presEffect ? 'active' : ''}`}
- onPointerDown={e => e.stopPropagation()}
- onClick={() => this.updateEffect(PresEffect.None)}>
- None
- </div>
- <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Fade ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Fade)}>
- Fade In
- </div>
- <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Flip ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Flip)}>
- Flip
- </div>
- <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Rotate ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Rotate)}>
- Rotate
- </div>
- <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Bounce ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Bounce)}>
- Bounce
- </div>
- <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Roll ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Roll)}>
- Roll
- </div>
+ <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
+ <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this._openEffectDropdown ? 'grid' : 'none' }} onPointerDown={e => e.stopPropagation()}>
+ {presEffect(PresEffect.None)}
+ {presEffect(PresEffect.Fade)}
+ {presEffect(PresEffect.Flip)}
+ {presEffect(PresEffect.Rotate)}
+ {presEffect(PresEffect.Bounce)}
+ {presEffect(PresEffect.Roll)}
</div>
</div>
<div className="ribbon-doubleButton" style={{ display: effect === 'None' ? 'none' : 'inline-flex' }}>
<div className="presBox-subheading">Effect direction</div>
- <div className="ribbon-property">{this.effectDirection}</div>
+ <div className="ribbon-property">{StrCast(this.activeItem.presEffectDirection)}</div>
</div>
<div className="effectDirection" style={{ display: effect === 'None' ? 'none' : 'grid', width: 40 }}>
- <Tooltip title={<div className="dash-tooltip">{'Enter from left'}</div>}>
- <div
- style={{ gridColumn: 1, gridRow: 2, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Left ? Colors.LIGHT_BLUE : 'black', cursor: 'pointer' }}
- onClick={() => this.updateEffectDirection(PresEffect.Left)}>
- <FontAwesomeIcon icon={'angle-right'} />
- </div>
- </Tooltip>
- <Tooltip title={<div className="dash-tooltip">{'Enter from right'}</div>}>
- <div
- style={{ gridColumn: 3, gridRow: 2, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Right ? Colors.LIGHT_BLUE : 'black', cursor: 'pointer' }}
- onClick={() => this.updateEffectDirection(PresEffect.Right)}>
- <FontAwesomeIcon icon={'angle-left'} />
- </div>
- </Tooltip>
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Enter from top'}</div>
- </>
- }>
- <div
- style={{ gridColumn: 2, gridRow: 1, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Top ? Colors.LIGHT_BLUE : 'black', cursor: 'pointer' }}
- onClick={() => this.updateEffectDirection(PresEffect.Top)}>
- <FontAwesomeIcon icon={'angle-down'} />
- </div>
- </Tooltip>
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Enter from bottom'}</div>
- </>
- }>
- <div
- style={{ gridColumn: 2, gridRow: 3, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Bottom ? Colors.LIGHT_BLUE : 'black', cursor: 'pointer' }}
- onClick={() => this.updateEffectDirection(PresEffect.Bottom)}>
- <FontAwesomeIcon icon={'angle-up'} />
- </div>
- </Tooltip>
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Enter from center'}</div>
- </>
- }>
- <div
- style={{
- gridColumn: 2,
- gridRow: 2,
- width: 10,
- height: 10,
- alignSelf: 'center',
- justifySelf: 'center',
- border: this.activeItem.presEffectDirection === PresEffect.Center || !this.activeItem.presEffectDirection ? `solid 2px ${Colors.LIGHT_BLUE}` : 'solid 2px black',
- borderRadius: '100%',
- cursor: 'pointer',
- }}
- onClick={() => this.updateEffectDirection(PresEffect.Center)}></div>
- </Tooltip>
+ {presDirection(PresEffect.Left, 'angle-right', 1, 2, {})}
+ {presDirection(PresEffect.Right, 'angle-left', 3, 2, {})}
+ {presDirection(PresEffect.Top, 'angle-down', 2, 1, {})}
+ {presDirection(PresEffect.Bottom, 'angle-up', 2, 3, {})}
+ {presDirection(PresEffect.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
</div>
</div>
)}
@@ -1560,243 +1406,27 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
}
- @computed get effectDirection(): string {
- let effect = '';
- switch (this.activeItem.presEffectDirection) {
- case 'left':
- effect = 'Enter from left';
- break;
- case 'right':
- effect = 'Enter from right';
- break;
- case 'top':
- effect = 'Enter from top';
- break;
- case 'bottom':
- effect = 'Enter from bottom';
- break;
- default:
- effect = 'Enter from center';
- break;
- }
- return effect;
- }
-
@undoBatch
@action
applyTo = (array: Doc[]) => {
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
- this.updateMovement(activeItem.presMovement, true);
- this.updateEffect(activeItem.presEffect, true);
- this.updateEffectDirection(activeItem.presEffectDirection, true);
- array.forEach(doc => {
- const curDoc = Cast(doc, Doc, null);
- const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
- if (tagDoc && targetDoc) {
- curDoc.presTransition = activeItem.presTransition;
- curDoc.presDuration = activeItem.presDuration;
- curDoc.presHideBefore = activeItem.presHideBefore;
- curDoc.presHideAfter = activeItem.presHideAfter;
- }
+ this.updateMovement(this.activeItem.presMovement as PresMovement, true);
+ this.updateEffect(this.activeItem.presEffect as PresEffect, true);
+ this.updateEffectDirection(this.activeItem.presEffectDirection as PresEffect, true);
+ const { presTransition, presDuration, presHideBefore, presHideAfter } = this.activeItem;
+ array.forEach(curDoc => {
+ curDoc.presTransition = presTransition;
+ curDoc.presDuration = presDuration;
+ curDoc.presHideBefore = presHideBefore;
+ curDoc.presHideAfter = presHideAfter;
});
};
- @computed get presPinViewOptionsDropdown() {
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
- const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: 'auto', width: 16, filter: 'invert(1)' }} />;
- return (
- <>
- {this.panable || this.scrollable || this.targetDoc.type === DocumentType.COMPARISON ? 'Pinned view' : null}
- <div className="ribbon-doubleButton">
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{activeItem.presPinView ? 'Turn off pin with view' : 'Turn on pin with view'}</div>
- </>
- }>
- <div
- className="ribbon-toggle"
- style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? Colors.LIGHT_BLUE : '' }}
- onClick={() => {
- activeItem.presPinView = !activeItem.presPinView;
- targetDoc.presPinView = activeItem.presPinView;
- if (activeItem.presPinView) {
- if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking) {
- const scroll = targetDoc._scrollTop;
- activeItem.presPinView = true;
- activeItem.presPinViewScroll = scroll;
- } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
- activeItem.presStartTime = targetDoc._currentTimecode;
- activeItem.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1;
- } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG) {
- const x = targetDoc._panX;
- const y = targetDoc._panY;
- const scale = targetDoc._viewScale;
- activeItem.presPinView = true;
- activeItem.presPinViewX = x;
- activeItem.presPinViewY = y;
- activeItem.presPinViewScale = scale;
- } else if (targetDoc.type === DocumentType.COMPARISON) {
- const width = targetDoc._clipWidth;
- activeItem.presPinClipWidth = width;
- activeItem.presPinView = true;
- }
- }
- }}>
- {presPinWithViewIcon}
- </div>
- </Tooltip>
- {activeItem.presPinView ? (
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Update the pinned view with the view of the selected document'}</div>
- </>
- }>
- <div
- className="ribbon-button"
- onClick={() => {
- if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) {
- const scroll = targetDoc._scrollTop;
- activeItem.presPinViewScroll = scroll;
- } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
- activeItem.presStartTime = targetDoc._currentTimecode;
- activeItem.presStartTime = NumCast(targetDoc._currentTimecode) + 0.1;
- } else if (targetDoc.type === DocumentType.COMPARISON) {
- const clipWidth = targetDoc._clipWidth;
- activeItem.presPinClipWidth = clipWidth;
- } else {
- const x = targetDoc._panX;
- const y = targetDoc._panY;
- const scale = targetDoc._viewScale;
- activeItem.presPinViewX = x;
- activeItem.presPinViewY = y;
- activeItem.presPinViewScale = scale;
- }
- }}>
- Update
- </div>
- </Tooltip>
- ) : null}
- </div>
- </>
- );
- }
-
- @computed get panOptionsDropdown() {
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
- return (
- <>
- {this.panable ? (
- <div style={{ display: activeItem.presPinView ? 'block' : 'none' }}>
- <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
- <div className="presBox-subheading">Pan X</div>
- <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
- <input
- className="presBox-input"
- style={{ textAlign: 'left', width: 50 }}
- type="number"
- value={NumCast(activeItem.presPinViewX)}
- onKeyDown={e => e.stopPropagation()}
- onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
- const val = e.target.value;
- activeItem.presPinViewX = Number(val);
- })}
- />
- </div>
- </div>
- <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
- <div className="presBox-subheading">Pan Y</div>
- <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
- <input
- className="presBox-input"
- style={{ textAlign: 'left', width: 50 }}
- type="number"
- value={NumCast(activeItem.presPinViewY)}
- onKeyDown={e => e.stopPropagation()}
- onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
- const val = e.target.value;
- activeItem.presPinViewY = Number(val);
- })}
- />
- </div>
- </div>
- <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
- <div className="presBox-subheading">Scale</div>
- <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
- <input
- className="presBox-input"
- style={{ textAlign: 'left', width: 50 }}
- type="number"
- value={NumCast(activeItem.presPinViewScale)}
- onKeyDown={e => e.stopPropagation()}
- onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
- const val = e.target.value;
- activeItem.presPinViewScale = Number(val);
- })}
- />
- </div>
- </div>
- </div>
- ) : null}
- </>
- );
- }
-
- @computed get scrollOptionsDropdown() {
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
- return (
- <>
- {this.scrollable ? (
- <div style={{ display: activeItem.presPinView ? 'block' : 'none' }}>
- <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
- <div className="presBox-subheading">Scroll</div>
- <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
- <input
- className="presBox-input"
- style={{ textAlign: 'left', width: 50 }}
- type="number"
- value={NumCast(activeItem.presPinViewScroll)}
- onKeyDown={e => e.stopPropagation()}
- onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
- const val = e.target.value;
- activeItem.presPinViewScroll = Number(val);
- })}
- />
- </div>
- </div>
- </div>
- ) : null}
- </>
- );
- }
-
- @computed get mediaStopSlides() {
- const activeItem: Doc = this.activeItem;
- const list = this.childDocs.map((doc, i) => {
- if (i > this.itemIndex) {
- return (
- <option>
- {i + 1}. {StrCast(doc.title)}
- </option>
- );
- }
- });
- return list;
- }
-
@computed get mediaOptionsDropdown() {
const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
const clipStart: number = NumCast(activeItem.clipStart);
const clipEnd: number = NumCast(activeItem.clipEnd);
- const duration = Math.round(NumCast(activeItem[`${Doc.LayoutFieldKey(activeItem)}-duration`]) * 10);
const mediaStopDocInd: number = NumCast(activeItem.mediaStopDoc);
- const mediaStopDocStr: string = mediaStopDocInd ? mediaStopDocInd + '. ' + this.childDocs[mediaStopDocInd - 1].title : '';
if (activeItem && targetDoc) {
return (
<div>
@@ -1967,55 +1597,53 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get newDocumentToolbarDropdown() {
return (
- <div>
- <div
- className={'presBox-toolbar-dropdown'}
- style={{ display: this.newDocumentTools && this.layoutDoc.presStatus === 'edit' ? 'inline-flex' : 'none' }}
- onClick={e => e.stopPropagation()}
- onPointerUp={e => e.stopPropagation()}
- onPointerDown={e => e.stopPropagation()}>
- <div className="layout-container" style={{ height: 'max-content' }}>
- <div
- className="layout"
- style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
- onClick={action(() => {
- this.layout = 'blank';
- this.createNewSlide(this.layout);
- })}
- />
- <div
- className="layout"
- style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
- onClick={action(() => {
- this.layout = 'title';
- this.createNewSlide(this.layout);
- })}>
- <div className="title">Title</div>
- <div className="subtitle">Subtitle</div>
- </div>
- <div
- className="layout"
- style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
- onClick={action(() => {
- this.layout = 'header';
- this.createNewSlide(this.layout);
- })}>
- <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>
- Section header
- </div>
+ <div
+ className={'presBox-toolbar-dropdown'}
+ style={{ display: this._newDocumentTools && this.layoutDoc.presStatus === 'edit' ? 'inline-flex' : 'none' }}
+ onClick={e => e.stopPropagation()}
+ onPointerUp={e => e.stopPropagation()}
+ onPointerDown={e => e.stopPropagation()}>
+ <div className="layout-container" style={{ height: 'max-content' }}>
+ <div
+ className="layout"
+ style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
+ onClick={action(() => {
+ this.layout = 'blank';
+ this.createNewSlide(this.layout);
+ })}
+ />
+ <div
+ className="layout"
+ style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
+ onClick={action(() => {
+ this.layout = 'title';
+ this.createNewSlide(this.layout);
+ })}>
+ <div className="title">Title</div>
+ <div className="subtitle">Subtitle</div>
+ </div>
+ <div
+ className="layout"
+ style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
+ onClick={action(() => {
+ this.layout = 'header';
+ this.createNewSlide(this.layout);
+ })}>
+ <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>
+ Section header
</div>
- <div
- className="layout"
- style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
- onClick={action(() => {
- this.layout = 'content';
- this.createNewSlide(this.layout);
- })}>
- <div className="title" style={{ alignSelf: 'center' }}>
- Title
- </div>
- <div className="content">Text goes here</div>
+ </div>
+ <div
+ className="layout"
+ style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
+ onClick={action(() => {
+ this.layout = 'content';
+ this.createNewSlide(this.layout);
+ })}>
+ <div className="title" style={{ alignSelf: 'center' }}>
+ Title
</div>
+ <div className="content">Text goes here</div>
</div>
</div>
</div>
@@ -2029,73 +1657,71 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get newDocumentDropdown() {
return (
- <div>
- <div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
- <div className="ribbon-box">
- Slide Title: <br></br>
- <input
- className="ribbon-textInput"
- placeholder="..."
- type="text"
- name="fname"
- onChange={e => {
- e.stopPropagation();
- e.preventDefault();
- runInAction(() => (this.title = e.target.value));
- }}></input>
+ <div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
+ <div className="ribbon-box">
+ Slide Title: <br></br>
+ <input
+ className="ribbon-textInput"
+ placeholder="..."
+ type="text"
+ name="fname"
+ onChange={e => {
+ e.stopPropagation();
+ e.preventDefault();
+ runInAction(() => (this.title = e.target.value));
+ }}></input>
+ </div>
+ <div className="ribbon-box">
+ Choose type:
+ <div className="ribbon-doubleButton">
+ <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? '' : Colors.LIGHT_BLUE }} onClick={action(() => (this.addFreeform = !this.addFreeform))}>
+ Text
+ </div>
+ <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? Colors.LIGHT_BLUE : '' }} onClick={action(() => (this.addFreeform = !this.addFreeform))}>
+ Freeform
+ </div>
</div>
- <div className="ribbon-box">
- Choose type:
- <div className="ribbon-doubleButton">
- <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? '' : Colors.LIGHT_BLUE }} onClick={action(() => (this.addFreeform = !this.addFreeform))}>
- Text
- </div>
- <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? Colors.LIGHT_BLUE : '' }} onClick={action(() => (this.addFreeform = !this.addFreeform))}>
- Freeform
+ </div>
+ <div className="ribbon-box" style={{ display: this.addFreeform ? 'grid' : 'none' }}>
+ Preset layouts:
+ <div className="layout-container" style={{ height: this.openLayouts ? 'max-content' : '75px' }}>
+ <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'blank'))} />
+ <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'title'))}>
+ <div className="title">Title</div>
+ <div className="subtitle">Subtitle</div>
+ </div>
+ <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'header'))}>
+ <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>
+ Section header
</div>
</div>
- </div>
- <div className="ribbon-box" style={{ display: this.addFreeform ? 'grid' : 'none' }}>
- Preset layouts:
- <div className="layout-container" style={{ height: this.openLayouts ? 'max-content' : '75px' }}>
- <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'blank'))} />
- <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'title'))}>
- <div className="title">Title</div>
- <div className="subtitle">Subtitle</div>
+ <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'content'))}>
+ <div className="title" style={{ alignSelf: 'center' }}>
+ Title
</div>
- <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'header'))}>
- <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>
- Section header
- </div>
+ <div className="content">Text goes here</div>
+ </div>
+ <div className="layout" style={{ border: this.layout === 'twoColumns' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'twoColumns'))}>
+ <div className="title" style={{ alignSelf: 'center', gridColumn: '1/3' }}>
+ Title
</div>
- <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'content'))}>
- <div className="title" style={{ alignSelf: 'center' }}>
- Title
- </div>
- <div className="content">Text goes here</div>
+ <div className="content" style={{ gridColumn: 1, gridRow: 2 }}>
+ Column one text
</div>
- <div className="layout" style={{ border: this.layout === 'twoColumns' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'twoColumns'))}>
- <div className="title" style={{ alignSelf: 'center', gridColumn: '1/3' }}>
- Title
- </div>
- <div className="content" style={{ gridColumn: 1, gridRow: 2 }}>
- Column one text
- </div>
- <div className="content" style={{ gridColumn: 2, gridRow: 2 }}>
- Column two text
- </div>
+ <div className="content" style={{ gridColumn: 2, gridRow: 2 }}>
+ Column two text
</div>
</div>
- <div className="open-layout" onClick={action(() => (this.openLayouts = !this.openLayouts))}>
- <FontAwesomeIcon style={{ transition: 'all 0.3s', transform: this.openLayouts ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={'caret-down'} size={'lg'} />
- </div>
</div>
- <div className="ribbon-final-box">
- <div
- className={this.title !== '' && ((this.addFreeform && this.layout !== '') || !this.addFreeform) ? 'ribbon-final-button-hidden' : 'ribbon-final-button'}
- onClick={() => this.createNewSlide(this.layout, this.title, this.addFreeform)}>
- Create New Slide
- </div>
+ <div className="open-layout" onClick={action(() => (this.openLayouts = !this.openLayouts))}>
+ <FontAwesomeIcon style={{ transition: 'all 0.3s', transform: this.openLayouts ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={'caret-down'} size={'lg'} />
+ </div>
+ </div>
+ <div className="ribbon-final-box">
+ <div
+ className={this.title !== '' && ((this.addFreeform && this.layout !== '') || !this.addFreeform) ? 'ribbon-final-button-hidden' : 'ribbon-final-button'}
+ onClick={() => this.createNewSlide(this.layout, this.title, this.addFreeform)}>
+ Create New Slide
</div>
</div>
</div>
@@ -2122,48 +1748,29 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
createTemplate = (layout: string, input?: string) => {
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
- let x = 0;
- let y = 0;
- if (activeItem && targetDoc) {
- x = NumCast(targetDoc.x);
- y = NumCast(targetDoc.y) + NumCast(targetDoc._height) + 20;
- }
- let doc = undefined;
- const title = Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, _fontSize: '24pt' });
- const subtitle = Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, _fontSize: '16pt' });
- const header = Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, _fontSize: '20pt' });
- const contentTitle = Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, _fontSize: '24pt' });
- const content = Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, _fontSize: '14pt' });
- const content1 = Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, _fontSize: '14pt' });
- const content2 = Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, _fontSize: '14pt' });
+ const x = this.activeItem && this.targetDoc ? NumCast(this.targetDoc.x) : 0;
+ const y = this.activeItem && this.targetDoc ? NumCast(this.targetDoc.y) + NumCast(this.targetDoc._height) + 20 : 0;
+ const title = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, _fontSize: '24pt' });
+ const subtitle = () => Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, _fontSize: '16pt' });
+ const header = () => Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, _fontSize: '20pt' });
+ const contentTitle = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, _fontSize: '24pt' });
+ const content = () => Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, _fontSize: '14pt' });
+ const content1 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, _fontSize: '14pt' });
+ const content2 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, _fontSize: '14pt' });
+ // prettier-ignore
switch (layout) {
- case 'blank':
- doc = Docs.Create.FreeformDocument([], { title: input ? input : 'Blank slide', _width: 400, _height: 225, x: x, y: y });
- break;
- case 'title':
- doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : 'Title slide', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
- break;
- case 'header':
- doc = Docs.Create.FreeformDocument([header], { title: input ? input : 'Section header', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
- break;
- case 'content':
- doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : 'Title and content', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
- break;
- case 'twoColumns':
- doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
- break;
- default:
- break;
+ case 'blank': return Docs.Create.FreeformDocument([], { title: input ? input : 'Blank slide', _width: 400, _height: 225, x, y });
+ case 'title': return Docs.Create.FreeformDocument([title(), subtitle()], { title: input ? input : 'Title slide', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
+ case 'header': return Docs.Create.FreeformDocument([header()], { title: input ? input : 'Section header', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
+ case 'content': return Docs.Create.FreeformDocument([contentTitle(), content()], { title: input ? input : 'Title and content', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
+ case 'twoColumns': return Docs.Create.FreeformDocument([contentTitle(), content1(), content2()], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _fitContentsToBox: true, x, y })
}
- return doc;
};
// Dropdown that appears when the user wants to begin presenting (either minimize or sidebar view)
@computed get presentDropdown() {
return (
- <div className={`dropdown-play ${this.presentTools ? 'active' : ''}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
+ <div className={`dropdown-play ${this._presentTools ? 'active' : ''}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
<div
className="dropdown-play-button"
onClick={undoBatch(
@@ -2221,42 +1828,22 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
/**
* Returns the collection type as a string for headers
*/
- @computed get stringType(): string {
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
- let type: string = '';
- if (activeItem) {
- switch (targetDoc.type) {
- case DocumentType.PDF:
- type = 'PDF';
- break;
- case DocumentType.RTF:
- type = 'Text node';
- break;
- case DocumentType.COL:
- type = 'Collection';
- break;
- case DocumentType.AUDIO:
- type = 'Audio';
- break;
- case DocumentType.VID:
- type = 'Video';
- break;
- case DocumentType.IMG:
- type = 'Image';
- break;
- case DocumentType.WEB:
- type = 'Web page';
- break;
- case DocumentType.MAP:
- type = 'Map';
- break;
- default:
- type = 'Other node';
- break;
+ @computed get stringType() {
+ if (this.activeItem) {
+ // prettier-ignore
+ switch (this.targetDoc.type) {
+ case DocumentType.PDF: return 'PDF';
+ case DocumentType.RTF: return 'Text node';
+ case DocumentType.COL: return 'Collection';
+ case DocumentType.AUDIO: return 'Audio';
+ case DocumentType.VID: return 'Video';
+ case DocumentType.IMG: return 'Image';
+ case DocumentType.WEB: return 'Web page';
+ case DocumentType.MAP: return 'Map';
+ default: return 'Other node';
}
}
- return type;
+ return '';
}
@observable private openActiveColorPicker: boolean = false;
@@ -2269,115 +1856,109 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const activeFontColor = targetDoc['pres-text-color'] ? StrCast(targetDoc['pres-text-color']) : 'Black';
const viewedFontColor = targetDoc['pres-text-viewed-color'] ? StrCast(targetDoc['pres-text-viewed-color']) : 'Black';
return (
- <div>
- <div
- className={`presBox-ribbon ${this.progressivizeTools && this.layoutDoc.presStatus === 'edit' ? 'active' : ''}`}
- onClick={e => e.stopPropagation()}
- onPointerUp={e => e.stopPropagation()}
- onPointerDown={e => e.stopPropagation()}>
- <div className="ribbon-box">
- {this.stringType} selected
- <div
- className="ribbon-doubleButton"
- style={{
- borderTop: 'solid 1px darkgrey',
- display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.RTF ? 'inline-flex' : 'none',
- }}>
- <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeChild}>
- Contents
- </div>
- <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editProgressivize}>
- Edit
- </div>
- </div>
- <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? 'inline-flex' : 'none' }}>
- <div className="presBox-subheading">Active text color</div>
- <div
- className="ribbon-colorBox"
- style={{ backgroundColor: activeFontColor, height: 15, width: 15 }}
- onClick={action(() => {
- this.openActiveColorPicker = !this.openActiveColorPicker;
- })}></div>
+ <div className={`presBox-ribbon ${this._progressivizeTools && this.layoutDoc.presStatus === 'edit' ? 'active' : ''}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
+ <div className="ribbon-box">
+ {this.stringType} selected
+ <div
+ className="ribbon-doubleButton"
+ style={{
+ borderTop: 'solid 1px darkgrey',
+ display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.RTF ? 'inline-flex' : 'none',
+ }}>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeChild}>
+ Contents
</div>
- {this.activeColorPicker}
- <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? 'inline-flex' : 'none' }}>
- <div className="presBox-subheading">Viewed font color</div>
- <div className="ribbon-colorBox" style={{ backgroundColor: viewedFontColor, height: 15, width: 15 }} onClick={action(() => (this.openViewedColorPicker = !this.openViewedColorPicker))}></div>
+ <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editProgressivize}>
+ Edit
</div>
- {this.viewedColorPicker}
+ </div>
+ <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? 'inline-flex' : 'none' }}>
+ <div className="presBox-subheading">Active text color</div>
<div
- className="ribbon-doubleButton"
- style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? 'inline-flex' : 'none' }}>
- <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeZoom}>
- Zoom
- </div>
- <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editZoomProgressivize}>
- Edit
- </div>
+ className="ribbon-colorBox"
+ style={{ backgroundColor: activeFontColor, height: 15, width: 15 }}
+ onClick={action(() => {
+ this.openActiveColorPicker = !this.openActiveColorPicker;
+ })}></div>
+ </div>
+ {this.activeColorPicker}
+ <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? 'inline-flex' : 'none' }}>
+ <div className="presBox-subheading">Viewed font color</div>
+ <div className="ribbon-colorBox" style={{ backgroundColor: viewedFontColor, height: 15, width: 15 }} onClick={action(() => (this.openViewedColorPicker = !this.openViewedColorPicker))}></div>
+ </div>
+ {this.viewedColorPicker}
+ <div
+ className="ribbon-doubleButton"
+ style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? 'inline-flex' : 'none' }}>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeZoom}>
+ Zoom
</div>
- <div
- className="ribbon-doubleButton"
- style={{
- borderTop: 'solid 1px darkgrey',
- display: targetDoc._viewType === 'stacking' || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? 'inline-flex' : 'none',
- }}>
- <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeScroll}>
- Scroll
- </div>
- <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editScrollProgressivize}>
- Edit
- </div>
+ <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editZoomProgressivize}>
+ Edit
</div>
</div>
- <div className="ribbon-final-box">
- Frames
- <div className="ribbon-doubleButton">
- <div className="ribbon-frameSelector">
- <div
- key="back"
- title="back frame"
- className="backKeyframe"
- onClick={e => {
- e.stopPropagation();
- this.prevKeyframe(targetDoc, activeItem);
- }}>
- <FontAwesomeIcon icon={'caret-left'} size={'lg'} />
- </div>
- <div
- key="num"
- title="toggle view all"
- className="numKeyframe"
- style={{ color: targetDoc.keyFrameEditing ? 'white' : 'black', backgroundColor: targetDoc.keyFrameEditing ? Colors.MEDIUM_BLUE : Colors.LIGHT_BLUE }}
- onClick={action(() => (targetDoc.keyFrameEditing = !targetDoc.keyFrameEditing))}>
- {NumCast(targetDoc._currentFrame)}
- </div>
- <div
- key="fwd"
- title="forward frame"
- className="fwdKeyframe"
- onClick={e => {
- e.stopPropagation();
- this.nextKeyframe(targetDoc, activeItem);
- }}>
- <FontAwesomeIcon icon={'caret-right'} size={'lg'} />
- </div>
- </div>
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Last frame'}</div>
- </>
- }>
- <div className="ribbon-property">{NumCast(targetDoc.lastFrame)}</div>
- </Tooltip>
+ <div
+ className="ribbon-doubleButton"
+ style={{
+ borderTop: 'solid 1px darkgrey',
+ display: targetDoc._viewType === 'stacking' || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? 'inline-flex' : 'none',
+ }}>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeScroll}>
+ Scroll
</div>
- <div className="ribbon-frameList">
- {this.frameListHeader}
- {this.frameList}
+ <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editScrollProgressivize}>
+ Edit
</div>
- <div className="ribbon-toggle" style={{ height: 20, backgroundColor: Colors.LIGHT_BLUE }} onClick={() => console.log(' TODO: play frames')}>
- Play
+ </div>
+ </div>
+ <div className="ribbon-final-box">
+ Frames
+ <div className="ribbon-doubleButton">
+ <div className="ribbon-frameSelector">
+ <div
+ key="back"
+ title="back frame"
+ className="backKeyframe"
+ onClick={e => {
+ e.stopPropagation();
+ this.prevKeyframe(targetDoc, activeItem);
+ }}>
+ <FontAwesomeIcon icon={'caret-left'} size={'lg'} />
+ </div>
+ <div
+ key="num"
+ title="toggle view all"
+ className="numKeyframe"
+ style={{ color: targetDoc.keyFrameEditing ? 'white' : 'black', backgroundColor: targetDoc.keyFrameEditing ? Colors.MEDIUM_BLUE : Colors.LIGHT_BLUE }}
+ onClick={action(() => (targetDoc.keyFrameEditing = !targetDoc.keyFrameEditing))}>
+ {NumCast(targetDoc._currentFrame)}
+ </div>
+ <div
+ key="fwd"
+ title="forward frame"
+ className="fwdKeyframe"
+ onClick={e => {
+ e.stopPropagation();
+ this.nextKeyframe(targetDoc, activeItem);
+ }}>
+ <FontAwesomeIcon icon={'caret-right'} size={'lg'} />
+ </div>
</div>
+ <Tooltip
+ title={
+ <>
+ <div className="dash-tooltip">{'Last frame'}</div>
+ </>
+ }>
+ <div className="ribbon-property">{NumCast(targetDoc.lastFrame)}</div>
+ </Tooltip>
+ </div>
+ <div className="ribbon-frameList">
+ {this.frameListHeader}
+ {this.frameList}
+ </div>
+ <div className="ribbon-toggle" style={{ height: 20, backgroundColor: Colors.LIGHT_BLUE }} onClick={() => console.log(' TODO: play frames')}>
+ Play
</div>
</div>
</div>
@@ -2388,42 +1969,32 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@undoBatch
@action
switchActive = (color: ColorState) => {
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
- const val = String(color.hex);
- targetDoc['pres-text-color'] = val;
+ this.targetDoc['pres-text-color'] = String(color.hex);
return true;
};
@undoBatch
@action
switchPresented = (color: ColorState) => {
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
- const val = String(color.hex);
- targetDoc['pres-text-viewed-color'] = val;
+ this.targetDoc['pres-text-viewed-color'] = String(color.hex);
return true;
};
@computed get activeColorPicker() {
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
return !this.openActiveColorPicker ? null : (
<SketchPicker
onChange={this.switchActive}
presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
- color={StrCast(targetDoc['pres-text-color'])}
+ color={StrCast(this.targetDoc['pres-text-color'])}
/>
);
}
@computed get viewedColorPicker() {
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
return !this.openViewedColorPicker ? null : (
<SketchPicker
onChange={this.switchPresented}
presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
- color={StrCast(targetDoc['pres-text-viewed-color'])}
+ color={StrCast(this.targetDoc['pres-text-viewed-color'])}
/>
);
}
@@ -2466,12 +2037,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
//Toggle whether the user edits or not
@action
editScrollProgressivize = (e: React.MouseEvent) => {
- const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
if (!targetDoc.editScrollProgressivize) {
if (!targetDoc.scrollProgressivize) {
targetDoc.scrollProgressivize = true;
- activeItem.scrollProgressivize = true;
+ this.activeItem.scrollProgressivize = true;
}
targetDoc.editScrollProgressivize = true;
} else {
@@ -2483,8 +2053,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@action
progressivizeScroll = (e: React.MouseEvent) => {
e.stopPropagation();
- const activeItem: Doc = this.activeItem;
- activeItem.scrollProgressivize = !activeItem.scrollProgressivize;
+ this.activeItem.scrollProgressivize = !this.activeItem.scrollProgressivize;
const targetDoc: Doc = this.targetDoc;
targetDoc.scrollProgressivize = !targetDoc.scrollProgressivize;
// CollectionFreeFormDocumentView.setupScroll(targetDoc, NumCast(targetDoc._currentFrame));
@@ -2586,21 +2155,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
@observable
- toggleDisplayMovement = (doc: Doc) => {
- if (doc.displayMovement) doc.displayMovement = false;
- else doc.displayMovement = true;
- };
+ toggleDisplayMovement = (doc: Doc) => (doc.displayMovement = !doc.displayMovement);
@action
checkList = (doc: Doc, list: any): number => {
const x: List<number> = list;
- if (x && x.length >= NumCast(doc._currentFrame) + 1) {
+ if (x?.length >= NumCast(doc._currentFrame) + 1) {
return x[NumCast(doc._currentFrame)];
} else if (x) {
x.length = NumCast(doc._currentFrame) + 1;
x[NumCast(doc._currentFrame)] = x[NumCast(doc._currentFrame) - 1];
return x[NumCast(doc._currentFrame)];
- } else return 100;
+ }
+ return 100;
};
@computed get progressivizeChildDocs() {
@@ -2654,26 +2221,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
@action
- nextAppearFrame = (doc: Doc, i: number): void => {
- // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- // const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
- const appearFrame = Cast(doc.appearFrame, 'number', null);
- if (appearFrame === undefined) {
- doc.appearFrame = 0;
- }
- doc.appearFrame = appearFrame + 1;
+ nextAppearFrame = (doc: Doc, i: number) => {
+ doc.appearFrame = (Cast(doc.appearFrame, 'number', null) ?? 0) + 1;
this.updateOpacityList(doc['opacity-indexed'], NumCast(doc.appearFrame));
};
@action
- prevAppearFrame = (doc: Doc, i: number): void => {
- // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- // const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
- const appearFrame = Cast(doc.appearFrame, 'number', null);
- if (appearFrame === undefined) {
- doc.appearFrame = 0;
- }
- doc.appearFrame = Math.max(0, appearFrame - 1);
+ prevAppearFrame = (doc: Doc, i: number) => {
+ doc.appearFrame = Math.max(0, (Cast(doc.appearFrame, 'number', null) ?? 0) - 1);
this.updateOpacityList(doc['opacity-indexed'], NumCast(doc.appearFrame));
};
@@ -2702,31 +2257,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
};
- @computed get moreInfoDropdown() {
- return <div></div>;
- }
-
@computed
get toolbarWidth(): number {
- const width = this.props.PanelWidth();
- return width;
+ return this.props.PanelWidth();
}
@action
- toggleProperties = () => {
- if (SettingsManager.propertiesWidth > 0) {
- SettingsManager.propertiesWidth = 0;
- } else {
- SettingsManager.propertiesWidth = 250;
- }
- };
+ toggleProperties = () => (SettingsManager.propertiesWidth = SettingsManager.propertiesWidth > 0 ? 0 : 250);
@computed get toolbar() {
const propIcon = SettingsManager.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
const propTitle = SettingsManager.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel';
const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
const isMini: boolean = this.toolbarWidth <= 100;
- const presKeyEvents: boolean = this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.ActivePresentation;
const activeColor = Colors.LIGHT_BLUE;
const inactiveColor = Colors.WHITE;
return mode === CollectionViewType.Carousel3D ? null : (
@@ -2735,12 +2278,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<FontAwesomeIcon icon={"plus"} />
<FontAwesomeIcon className={`dropdown ${this.newDocumentTools ? "active" : ""}`} icon={"angle-down"} />
</div></Tooltip> */}
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'View paths'}</div>
- </>
- }>
+ <Tooltip title={<div className="dash-tooltip">{'View paths'}</div>}>
<div
style={{ opacity: this.childDocs.length > 1 && this.layoutDoc.presCollection ? 1 : 0.3, color: this._pathBoolean ? Colors.MEDIUM_BLUE : 'white', width: isMini ? '100%' : undefined }}
className={'toolbar-button'}
@@ -2759,22 +2297,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>
</Tooltip>
<div className="toolbar-divider" /> */}
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{presKeyEvents ? 'Keys are active' : 'Keys are not active - click anywhere on the presentation trail to activate keys'}</div>
- </>
- }>
- <div className="toolbar-button" style={{ cursor: presKeyEvents ? 'default' : 'pointer', position: 'absolute', right: 30, fontSize: 16 }}>
- <FontAwesomeIcon className={'toolbar-thumbtack'} icon={'keyboard'} style={{ color: presKeyEvents ? activeColor : inactiveColor }} />
+ <Tooltip title={<div className="dash-tooltip">{this._presKeyEvents ? 'Keys are active' : 'Keys are not active - click anywhere on the presentation trail to activate keys'}</div>}>
+ <div className="toolbar-button" style={{ cursor: this._presKeyEvents ? 'default' : 'pointer', position: 'absolute', right: 30, fontSize: 16 }}>
+ <FontAwesomeIcon className={'toolbar-thumbtack'} icon={'keyboard'} style={{ color: this._presKeyEvents ? activeColor : inactiveColor }} />
</div>
</Tooltip>
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{propTitle}</div>
- </>
- }>
+ <Tooltip title={<div className="dash-tooltip">{propTitle}</div>}>
<div className="toolbar-button" style={{ position: 'absolute', right: 4, fontSize: 16 }} onClick={this.toggleProperties}>
<FontAwesomeIcon className={'toolbar-thumbtack'} icon={propIcon} style={{ color: SettingsManager.propertiesWidth > 0 ? activeColor : inactiveColor }} />
</div>
@@ -2797,14 +2325,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div className="presBox-buttons" style={{ display: !this.rootDoc._chromeHidden ? 'none' : undefined }}>
{isMini ? null : (
<select className="presBox-viewPicker" style={{ display: this.layoutDoc.presStatus === 'edit' ? 'block' : 'none' }} onPointerDown={e => e.stopPropagation()} onChange={this.viewChanged} value={mode}>
- <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Stacking}>
+ <option onPointerDown={StopEvent} value={CollectionViewType.Stacking}>
List
</option>
- <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Tree}>
+ <option onPointerDown={StopEvent} value={CollectionViewType.Tree}>
Tree
</option>
{Doc.noviceMode ? null : (
- <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>
+ <option onPointerDown={StopEvent} value={CollectionViewType.Carousel3D}>
3D Carousel
</option>
)}
@@ -2825,11 +2353,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>
{mode === CollectionViewType.Carousel3D || isMini ? null : (
<div
- className={`presBox-button-right ${this.presentTools ? 'active' : ''}`}
+ className={`presBox-button-right ${this._presentTools ? 'active' : ''}`}
onClick={action(() => {
- if (this.childDocs.length) this.presentTools = !this.presentTools;
+ if (this.childDocs.length) this._presentTools = !this._presentTools;
})}>
- <FontAwesomeIcon className="dropdown" style={{ margin: 0, transform: this.presentTools ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={'angle-down'} />
+ <FontAwesomeIcon className="dropdown" style={{ margin: 0, transform: this._presentTools ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={'angle-down'} />
{this.presentDropdown}
</div>
)}
@@ -2841,16 +2369,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
@action
- getList = (list: any): List<number> => {
- const x: List<number> = list;
- return x;
- };
+ getList = (list: any): List<number> => list;
@action
updateList = (list: any): List<number> => {
const targetDoc: Doc = this.targetDoc;
const x: List<number> = list;
- x.length + 1;
x[x.length - 1] = NumCast(targetDoc._scrollY);
return x;
};
@@ -2858,17 +2382,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@action
newFrame = () => {
const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
- const type: string = StrCast(targetDoc.type);
+ const type: string = StrCast(this.targetDoc.type);
if (!activeItem.frameList) activeItem.frameList = new List<number>();
switch (type) {
case DocumentType.PDF || DocumentType.RTF || DocumentType.WEB:
this.updateList(activeItem.frameList);
break;
- case DocumentType.COL:
- break;
- default:
- break;
}
};
@@ -2877,12 +2396,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div className="frameList-header">
&nbsp; Frames {this.panable ? <i>Panable</i> : this.scrollable ? <i>Scrollable</i> : null}
<div className={'frameList-headerButtons'}>
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Add frame by example'}</div>
- </>
- }>
+ <Tooltip title={<div className="dash-tooltip">{'Add frame by example'}</div>}>
<div
className={'headerButton'}
onClick={e => {
@@ -2892,12 +2406,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<FontAwesomeIcon icon={'plus'} onPointerDown={e => e.stopPropagation()} />
</div>
</Tooltip>
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Edit in collection'}</div>
- </>
- }>
+ <Tooltip title={<div className="dash-tooltip">{'Edit in collection'}</div>}>
<div
className={'headerButton'}
onClick={e => {
@@ -2913,27 +2422,24 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
@computed get frameList() {
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
- const frameList: List<number> = this.getList(activeItem.frameList);
- if (frameList) {
- const frameItems = frameList.map(value => <div className="framList-item"></div>);
- return <div className="frameList-container">{frameItems}</div>;
- } else return null;
+ const frameList: List<number> = this.getList(this.activeItem.frameList);
+ return !frameList ? null : (
+ <div className="frameList-container">
+ {frameList.map(value => (
+ <div className="framList-item" />
+ ))}
+ </div>
+ );
}
@computed get playButtonFrames() {
- const targetDoc: Doc = this.targetDoc;
- return (
- <>
- {this.targetDoc ? (
- <div className="presPanel-button-frame" style={{ display: targetDoc.lastFrame !== undefined && targetDoc.lastFrame >= 0 ? 'inline-flex' : 'none' }}>
- <div>{NumCast(targetDoc._currentFrame)}</div>
- <div className="presPanel-divider" style={{ border: 'solid 0.5px white', height: '60%' }}></div>
- <div>{NumCast(targetDoc.lastFrame)}</div>
- </div>
- ) : null}
- </>
+ const targetDoc = this.targetDoc;
+ return !this.targetDoc ? null : (
+ <div className="presPanel-button-frame" style={{ display: targetDoc.lastFrame !== undefined && targetDoc.lastFrame >= 0 ? 'inline-flex' : 'none' }}>
+ <div>{NumCast(targetDoc._currentFrame)}</div>
+ <div className="presPanel-divider" style={{ border: 'solid 0.5px white', height: '60%' }}></div>
+ <div>{NumCast(targetDoc.lastFrame)}</div>
+ </div>
);
}
@@ -2943,17 +2449,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
// Case 1: There are still other frames and should go through all frames before going to next slide
return (
<div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== 'edit' ? 'inline-flex' : 'none' }}>
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Loop'}</div>
- </>
- }>
+ <Tooltip title={<div className="dash-tooltip">{'Loop'}</div>}>
<div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : 'white' }} onClick={() => (this.layoutDoc.presLoop = !this.layoutDoc.presLoop)}>
<FontAwesomeIcon icon={'redo-alt'} />
</div>
</Tooltip>
- <div className="presPanel-divider"></div>
+ <div className="presPanel-divider" />
<div
className="presPanel-button"
style={{ opacity: presStart ? 0.4 : 1 }}
@@ -2966,12 +2467,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}}>
<FontAwesomeIcon icon={'arrow-left'} />
</div>
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>
- </>
- }>
+ <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}>
<div className="presPanel-button" onClick={this.startOrPause}>
<FontAwesomeIcon icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? 'pause' : 'play'} />
</div>
@@ -2989,12 +2485,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<FontAwesomeIcon icon={'arrow-right'} />
</div>
<div className="presPanel-divider"></div>
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Click to return to 1st slide'}</div>
- </>
- }>
+ <Tooltip title={<div className="dash-tooltip">{'Click to return to 1st slide'}</div>}>
<div className="presPanel-button" style={{ border: 'solid 1px white' }} onClick={() => this.gotoDocument(0, this.activeItem)}>
<b>1</b>
</div>
@@ -3056,9 +2547,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
@action
- startMarqueeCreateSlide = () => {
- PresBox.startMarquee = true;
- };
+ startMarqueeCreateSlide = () => (PresBox.startMarquee = true);
AddToMap = (treeViewDoc: Doc, index: number[]): Doc[] => {
var indexNum = 0;
@@ -3088,23 +2577,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
sort = (treeViewMap: Map<Doc, number>) => [...treeViewMap.entries()].sort((a: [Doc, number], b: [Doc, number]) => (a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0)).map(kv => kv[0]);
render() {
- // calling this method for keyEvents
- this.isPres;
// needed to ensure that the childDocs are loaded for looking up fields
this.childDocs.slice();
const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
- const presKeyEvents: boolean = this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.ActivePresentation;
const presEnd: boolean = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1;
const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0;
return DocListCast(Doc.MyOverlayDocs?.data).includes(this.rootDoc) ? (
- <div className="miniPres" onClick={e => e.stopPropagation()}>
- <div className="presPanelOverlay" style={{ display: 'inline-flex', height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Loop'}</div>
- </>
- }>
+ <div className="miniPres" onClick={e => e.stopPropagation()} onPointerEnter={action(e => (this._forceKeyEvents = true))}>
+ <div className="presPanelOverlay" style={{ display: 'inline-flex', height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: this._presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
+ <Tooltip title={<div className="dash-tooltip">{'Loop'}</div>}>
<div
className="presPanel-button"
style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }}
@@ -3116,12 +2597,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.prevClicked, false, false)}>
<FontAwesomeIcon icon={'arrow-left'} />
</div>
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>
- </>
- }>
+ <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}>
<div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.startOrPause, false, false)}>
<FontAwesomeIcon icon={this.layoutDoc.presStatus === 'auto' ? 'pause' : 'play'} />
</div>
@@ -3130,12 +2606,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<FontAwesomeIcon icon={'arrow-right'} />
</div>
<div className="presPanel-divider"></div>
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Click to return to 1st slide'}</div>
- </>
- }>
+ <Tooltip title={<div className="dash-tooltip">{'Click to return to 1st slide'}</div>}>
<div className="presPanel-button" style={{ border: 'solid 1px white' }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => this.gotoDocument(0, this.activeItem), false, false)}>
<b>1</b>
</div>
@@ -3201,5 +2672,17 @@ ScriptingGlobals.add(function navigateToDoc(bestTarget: Doc, activeItem: Doc) {
CollectionDockingView.AddSplit(doc, 'right');
finished?.();
};
- DocumentManager.Instance.jumpToDocument(bestTarget, true, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, () => PresBox.navigateToDoc(bestTarget, activeItem, true), undefined, true, NumCast(activeItem.presZoom));
+ DocumentManager.Instance.jumpToDocument(
+ bestTarget,
+ true,
+ openInTab,
+ srcContext ? [srcContext] : [],
+ undefined,
+ undefined,
+ undefined,
+ () => PresBox.restoreTargetDocView(bestTarget, activeItem, true),
+ undefined,
+ true,
+ NumCast(activeItem.presZoom)
+ );
});
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 1a2054676..d88ff45fa 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -3,7 +3,7 @@ import { Tooltip } from '@material-ui/core';
import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
-import { Id } from '../../../../fields/FieldSymbols';
+import { Copy, Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
import { Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
@@ -24,6 +24,8 @@ import { PresBox } from './PresBox';
import './PresElementBox.scss';
import { PresMovement } from './PresEnums';
import React = require('react');
+import { InkField } from '../../../../fields/InkField';
+import { RichTextField } from '../../../../fields/RichTextField';
/**
* This class models the view a document added to presentation will have in the presentation.
* It involves some functionality for its buttons and options.
@@ -47,6 +49,13 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get presStatus() {
return this.presBox.presStatus;
}
+ @computed get selectedArray() {
+ return this.presBoxView?.selectedArray;
+ }
+ @computed get presBoxView() {
+ const vpath = this.props.docViewPath();
+ return vpath.length > 1 ? (vpath[vpath.length - 2].ComponentView as PresBox) : undefined;
+ }
@computed get presBox() {
return (this.props.DocumentView?.().props.treeViewDoc ?? this.props.ContainingCollectionDoc)!;
}
@@ -72,9 +81,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
ScreenToLocalListTransform = (xCord: number, yCord: number) => [xCord, yCord];
@action
- presExpandDocumentClick = () => {
- this.rootDoc.presExpandInlineButton = !this.rootDoc.presExpandInlineButton;
- };
+ presExpandDocumentClick = () => (this.rootDoc.presExpandInlineButton = !this.rootDoc.presExpandInlineButton);
embedHeight = (): number => 97;
// embedWidth = () => this.props.PanelWidth();
@@ -129,10 +136,9 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div
className="presItem-groupSlide"
onClick={e => {
- console.log('Clicked on slide with index: ', ind);
e.stopPropagation();
e.preventDefault();
- PresBox.Instance.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
+ this.presBoxView?.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
this.presExpandDocumentClick();
}}>
<div className="presItem-groupNum">{`${ind + 1}.`}</div>
@@ -154,15 +160,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
));
return groupSlides;
}
- @computed get duration() {
- let durationInS: number;
- if (this.rootDoc.type === DocumentType.AUDIO || this.rootDoc.type === DocumentType.VID) {
- durationInS = NumCast(this.rootDoc.presEndTime) - NumCast(this.rootDoc.presStartTime);
- durationInS = Math.round(durationInS * 10) / 10;
- } else if (this.rootDoc.presDuration) durationInS = NumCast(this.rootDoc.presDuration) / 1000;
- else durationInS = 2;
- return 'D: ' + durationInS + 's';
- }
@computed get transition() {
let transitionInS: number;
@@ -181,15 +178,15 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
e.stopPropagation();
e.preventDefault();
if (element && !(e.ctrlKey || e.metaKey)) {
- if (PresBox.Instance._selectedArray.has(this.rootDoc)) {
- PresBox.Instance._selectedArray.size === 1 && PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
+ if (this.selectedArray?.has(this.rootDoc)) {
+ this.selectedArray.size === 1 && this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
setupMoveUpEvents(this, e, this.startDrag, emptyFunction, emptyFunction);
} else {
setupMoveUpEvents(
this,
e,
(e: PointerEvent) => {
- PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
+ this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
return this.startDrag(e);
},
emptyFunction,
@@ -199,19 +196,14 @@ 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
*/
startDrag = (e: PointerEvent) => {
const miniView: boolean = this.toolbarWidth <= 100;
const activeItem = this.rootDoc;
- const dragArray = PresBox.Instance._dragArray;
- const dragData = new DragManager.DocumentDragData(PresBox.Instance.sortArray());
+ const dragArray = this.presBoxView?._dragArray ?? [];
+ const dragData = new DragManager.DocumentDragData(this.presBoxView?.sortArray() ?? []);
if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.rootDoc);
dragData.dropAction = 'move';
dragData.treeViewDoc = this.props.docViewPath().lastElement()?.props.treeViewDoc;
@@ -219,12 +211,14 @@ 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';
- doc.innerText = 'Move ' + PresBox.Instance._selectedArray.size + ' slides';
+ doc.innerText = 'Move ' + this.selectedArray?.size + ' slides';
doc.style.position = 'absolute';
doc.style.top = e.clientY + 'px';
doc.style.left = e.clientX - 50 + 'px';
@@ -253,10 +247,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
onPointerMove = (e: PointerEvent) => {
const slide = this._itemRef.current!;
- let dragIsPresItem: boolean = DragManager.docsBeingDragged.length > 0 ? true : false;
- for (const doc of DragManager.docsBeingDragged) {
- if (!doc.presentationTargetDoc) dragIsPresItem = false;
- }
+ const dragIsPresItem = DragManager.docsBeingDragged.some(d => d.presentationTargetDoc);
if (slide && dragIsPresItem) {
const rect = slide.getBoundingClientRect();
const y = e.clientY - rect.top; //y position within the element.
@@ -289,10 +280,11 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
@undoBatch
removeItem = action((e: React.MouseEvent) => {
e.stopPropagation();
- this.props.removeDocument?.(this.rootDoc);
- if (PresBox.Instance._selectedArray.has(this.rootDoc)) {
- PresBox.Instance._selectedArray.delete(this.rootDoc);
+ if (this.indexInPres < (this.presBoxView?.itemIndex || 0)) {
+ this.presBox.itemIndex = (this.presBoxView?.itemIndex || 0) - 1;
}
+ this.props.removeDocument?.(this.rootDoc);
+ this.presBoxView?.removeFromSelectedArray(this.rootDoc);
this.removeAllRecordingInOverlay();
});
@@ -319,6 +311,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
case DocumentType.RTF:
const scroll = targetDoc._scrollTop;
activeItem.presPinViewScroll = scroll;
+ if (targetDoc.type === DocumentType.RTF) {
+ activeItem.presData = targetDoc.text instanceof RichTextField ? targetDoc.text[Copy]() : targetDoc.text;
+ }
+ break;
+ case DocumentType.INK:
+ activeItem.presData = targetDoc.data instanceof InkField ? targetDoc.data[Copy]() : targetDoc.data;
break;
case DocumentType.VID:
case DocumentType.AUDIO:
@@ -336,6 +334,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
activeItem.presPinViewY = y;
activeItem.presPinViewScale = scale;
}
+
+ activeItem.presX = NumCast(targetDoc.x);
+ activeItem.presY = NumCast(targetDoc.y);
+ activeItem.presRot = NumCast(targetDoc.jitterRotation);
+ activeItem.presWidth = NumCast(targetDoc.width);
+ activeItem.presHeight = NumCast(targetDoc.height);
};
@computed get recordingIsInOverlay() {
@@ -380,7 +384,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
hideRecording = (e: React.MouseEvent, iconClick: boolean = false) => {
e.stopPropagation();
this.removeAllRecordingInOverlay();
-
// if (iconClick) PresElementBox.showVideo = false;
};
@@ -400,7 +403,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
@undoBatch
@action
- startRecording = (activeItem: Doc) => {
+ startRecording = (e: React.MouseEvent, activeItem: Doc) => {
+ e.stopPropagation();
console.log('start recording', 'activeItem', activeItem);
if (PresElementBox.videoIsRecorded(activeItem)) {
// if we already have an existing recording
@@ -444,7 +448,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
@computed get mainItem() {
- const isSelected: boolean = PresBox.Instance?._selectedArray.has(this.rootDoc);
+ const isSelected: boolean = this.selectedArray?.has(this.rootDoc) ? true : false;
const toolbarWidth: number = this.toolbarWidth;
const showMore: boolean = this.toolbarWidth >= 300;
const miniView: boolean = this.toolbarWidth <= 110;
@@ -466,109 +470,71 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
onClick={e => {
e.stopPropagation();
e.preventDefault();
- PresBox.Instance.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
+ this.presBoxView?.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
this.showRecording(activeItem);
}}
onDoubleClick={action(e => {
this.toggleProperties();
- PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true);
+ this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true);
})}
onPointerOver={this.onPointerOver}
onPointerLeave={this.onPointerLeave}
- onPointerDown={this.headerDown}
- onPointerUp={this.headerUp}>
- {/* {miniView ?
- // when width is LESS than 110 px
- <div className={`presItem-miniSlide ${isSelected ? "active" : ""}`} ref={miniView ? this._dragRef : null}>
- {`${this.indexInPres + 1}.`}
- </div>
- :
- // when width is MORE than 110 px
- <div className="presItem-number">
- {`${this.indexInPres + 1}.`}
- </div>} */}
- {/* <div className="presItem-number">
- {`${this.indexInPres + 1}.`}
- </div> */}
- {miniView ? null : (
+ onPointerDown={this.headerDown}>
+ {miniView ? (
+ <div className={`presItem-miniSlide ${isSelected ? 'active' : ''}`} ref={this._dragRef}>
+ {`${this.indexInPres + 1}.`}
+ </div>
+ ) : (
<div
- ref={miniView ? null : this._dragRef}
+ ref={this._dragRef}
className={`presItem-slide ${isSelected ? 'active' : ''}`}
style={{
backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
boxShadow: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isSelected ? '0 0 0px 1.5px' + presBoxColor : undefined) : undefined,
}}>
- <div className="presItem-name" style={{ maxWidth: showMore ? toolbarWidth - 195 : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }}>
+ <div
+ className="presItem-name"
+ style={{
+ pointerEvents: isSelected ? undefined : 'none',
+ maxWidth: showMore ? toolbarWidth - 195 : toolbarWidth - 105,
+ cursor: isSelected ? 'text' : 'grab',
+ }}>
<div>{`${this.indexInPres + 1}. `}</div>
<EditableView ref={this._titleRef} editing={!isSelected ? false : undefined} contents={activeItem.title} overflow={'ellipsis'} GetValue={() => StrCast(activeItem.title)} SetValue={this.onSetValue} />
</div>
{/* <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip> */}
{/* <Tooltip title={<><div className="dash-tooltip">{"Duration"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.duration}</div></Tooltip> */}
- <div className={'presItem-slideButtons'}>
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Update view'}</div>
- </>
- }>
+ <div className="presItem-slideButtons">
+ <Tooltip title={<div className="dash-tooltip">Update view</div>}>
<div className="slideButton" onClick={() => this.updateView(targetDoc, activeItem)} style={{ fontWeight: 700, display: activeItem.presPinView ? 'flex' : 'none' }}>
V
</div>
</Tooltip>
-
- {this.recordingIsInOverlay ? (
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Hide Recording'}</div>
- </>
- }>
- <div className="slideButton" onClick={e => this.hideRecording(e, true)} style={{ fontWeight: 700 }}>
- <FontAwesomeIcon icon={'video-slash'} onPointerDown={e => e.stopPropagation()} />
- </div>
- </Tooltip>
- ) : (
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{`${PresElementBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}</div>
- </>
- }>
- <div
- className="slideButton"
- onClick={e => {
- e.stopPropagation();
- this.startRecording(activeItem);
- }}
- style={{ fontWeight: 700 }}>
- <FontAwesomeIcon icon={'video'} onPointerDown={e => e.stopPropagation()} />
+ <Tooltip title={<div className="dash-tooltip">{this.recordingIsInOverlay ? 'Hide Recording' : `${PresElementBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}</div>}>
+ <div className="slideButton" onClick={e => (this.recordingIsInOverlay ? this.hideRecording(e, true) : this.startRecording(e, activeItem))} style={{ fontWeight: 700 }}>
+ <FontAwesomeIcon icon={`video${this.recordingIsInOverlay ? '-slash' : ''}`} onPointerDown={e => e.stopPropagation()} />
+ </div>
+ </Tooltip>
+ <Tooltip title={<div className="dash-tooltip">{activeItem.groupWithUp ? 'Ungroup' : 'Group with up'}</div>}>
+ <div
+ className="slideButton"
+ onClick={() => (activeItem.groupWithUp = !activeItem.groupWithUp)}
+ style={{
+ display: this.indexInPres === 0 ? 'none' : '',
+ zIndex: 1000 - this.indexInPres,
+ fontWeight: 700,
+ backgroundColor: activeItem.groupWithUp ? (presColorBool ? presBoxColor : Colors.MEDIUM_BLUE) : undefined,
+ height: activeItem.groupWithUp ? 53 : 18,
+ transform: activeItem.groupWithUp ? 'translate(0, -17px)' : undefined,
+ }}>
+ <div style={{ transform: activeItem.groupWithUp ? 'rotate(180deg) translate(0, -17.5px)' : 'rotate(0deg)' }}>
+ <FontAwesomeIcon icon={'arrow-up'} onPointerDown={e => e.stopPropagation()} />
</div>
- </Tooltip>
- )}
-
- {/* {this.indexInPres === 0 ? (null) : <Tooltip title={<><div className="dash-tooltip">{activeItem.groupWithUp ? "Ungroup" : "Group with up"}</div></>}>
- <div className="slideButton"
- onClick={() => activeItem.groupWithUp = !activeItem.groupWithUp}
- style={{
- zIndex: 1000 - this.indexInPres,
- fontWeight: 700,
- backgroundColor: activeItem.groupWithUp ? presColorBool ? presBoxColor : Colors.MEDIUM_BLUE : undefined,
- height: activeItem.groupWithUp ? 53 : 18,
- transform: activeItem.groupWithUp ? "translate(0, -17px)" : undefined
- }}>
- <div style={{ transform: activeItem.groupWithUp ? "rotate(180deg) translate(0, -17.5px)" : "rotate(0deg)" }}>
- <FontAwesomeIcon icon={"arrow-up"} onPointerDown={e => e.stopPropagation()} />
- </div>
- </div>
- </Tooltip>} */}
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? 'Minimize' : 'Expand'}</div>
- </>
- }>
+ </div>
+ </Tooltip>
+ <Tooltip title={<div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? 'Minimize' : 'Expand'}</div>}>
<div
- className={'slideButton'}
+ className="slideButton"
onClick={e => {
e.stopPropagation();
this.presExpandDocumentClick();
@@ -576,18 +542,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
<FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? 'eye-slash' : 'eye'} onPointerDown={e => e.stopPropagation()} />
</div>
</Tooltip>
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Remove from presentation'}</div>
- </>
- }>
+ <Tooltip title={<div className="dash-tooltip">Remove from presentation</div>}>
<div className={'slideButton'} onClick={this.removeItem}>
<FontAwesomeIcon icon={'trash'} onPointerDown={e => e.stopPropagation()} />
</div>
</Tooltip>
</div>
- {/* <div className="presItem-docName" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105 }}>{activeItem.presPinView ? (<><i>View of </i> {targetDoc.title}</>) : targetDoc.title}</div> */}
{this.renderEmbeddedInline}
</div>
)}
diff --git a/src/client/views/nodes/trails/PresEnums.ts b/src/client/views/nodes/trails/PresEnums.ts
index 93ab323fb..c6a222c3a 100644
--- a/src/client/views/nodes/trails/PresEnums.ts
+++ b/src/client/views/nodes/trails/PresEnums.ts
@@ -1,28 +1,28 @@
export enum PresMovement {
- Zoom = "zoom",
- Pan = "pan",
- Jump = "jump",
- None = "none",
+ Zoom = 'zoom',
+ Pan = 'pan',
+ Jump = 'jump',
+ None = 'none',
}
export enum PresEffect {
- Zoom = "Zoom",
- Lightspeed = "Lightspeed",
- Fade = "Fade in",
- Flip = "Flip",
- Rotate = "Rotate",
- Bounce = "Bounce",
- Roll = "Roll",
- None = "None",
- Left = "left",
- Right = "right",
- Center = "center",
- Top = "top",
- Bottom = "bottom"
+ Zoom = 'Zoom',
+ Lightspeed = 'Lightspeed',
+ Fade = 'Fade in',
+ Flip = 'Flip',
+ Rotate = 'Rotate',
+ Bounce = 'Bounce',
+ Roll = 'Roll',
+ None = 'None',
+ Left = 'Enter from left',
+ Right = 'Enter from right',
+ Center = 'Enter from center',
+ Top = 'Enter from Top',
+ Bottom = 'Enter from bottom',
}
export enum PresStatus {
- Autoplay = "auto",
- Manual = "manual",
- Edit = "edit"
-} \ No newline at end of file
+ Autoplay = 'auto',
+ Manual = 'manual',
+ Edit = 'edit',
+}