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/AudioBox.tsx8
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx66
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx2
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx4
-rw-r--r--src/client/views/nodes/DocumentLinksButton.scss34
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx70
-rw-r--r--src/client/views/nodes/DocumentView.scss24
-rw-r--r--src/client/views/nodes/DocumentView.tsx430
-rw-r--r--src/client/views/nodes/FilterBox.tsx2
-rw-r--r--src/client/views/nodes/FunctionPlotBox.tsx3
-rw-r--r--src/client/views/nodes/ImageBox.scss18
-rw-r--r--src/client/views/nodes/ImageBox.tsx74
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx5
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx65
-rw-r--r--src/client/views/nodes/LabelBox.tsx140
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx110
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx54
-rw-r--r--src/client/views/nodes/LoadingBox.scss15
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx12
-rw-r--r--src/client/views/nodes/PDFBox.tsx45
-rw-r--r--src/client/views/nodes/ScriptingBox.tsx2
-rw-r--r--src/client/views/nodes/VideoBox.tsx12
-rw-r--r--src/client/views/nodes/WebBox.scss1
-rw-r--r--src/client/views/nodes/WebBox.tsx55
-rw-r--r--src/client/views/nodes/button/FontIconBox.tsx42
-rw-r--r--src/client/views/nodes/formattedText/DashDocCommentView.tsx16
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx13
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.scss10
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx51
-rw-r--r--src/client/views/nodes/formattedText/EquationView.tsx23
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss6
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx134
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx14
-rw-r--r--src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts3
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts7
-rw-r--r--src/client/views/nodes/formattedText/SummaryView.tsx10
-rw-r--r--src/client/views/nodes/formattedText/marks_rts.ts20
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts1
-rw-r--r--src/client/views/nodes/trails/PresBox.scss26
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx1443
-rw-r--r--src/client/views/nodes/trails/PresElementBox.tsx17
-rw-r--r--src/client/views/nodes/trails/PresEnums.ts5
42 files changed, 1465 insertions, 1627 deletions
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 497b4993c..d95668c89 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -70,6 +70,12 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@observable _muted: boolean = false;
@observable _paused: boolean = false; // is recording paused
// @observable rawDuration: number = 0; // computed from the length of the audio element when loaded
+
+ constructor(props: any) {
+ super(props);
+ this.props.setContentView?.(this);
+ }
+
@computed get recordingStart() {
return DateCast(this.dataDoc[this.fieldKey + '-recordingStart'])?.date.getTime();
}
@@ -111,8 +117,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@action
componentDidMount() {
- this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
-
if (this.path) {
this.mediaState = media_state.Paused;
this.setPlayheadTime(NumCast(this.layoutDoc.clipStart));
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 260c98816..f8ef87fb1 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -16,7 +16,7 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm/Collec
import { DocComponent } from '../DocComponent';
import { StyleProp } from '../StyleProvider';
import './CollectionFreeFormDocumentView.scss';
-import { DocumentView, DocumentViewProps } from './DocumentView';
+import { DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
import React = require('react');
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
@@ -24,7 +24,6 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
sizeProvider?: (doc: Doc, replica: string) => { width: number; height: number } | undefined;
renderCutoffProvider: (doc: Doc) => boolean;
zIndex?: number;
- highlight?: boolean;
rotation: number;
dataTransition?: string;
replica: string;
@@ -75,9 +74,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
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);
}
@@ -119,45 +115,30 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
});
}
- public static updateKeyframe(docs: Doc[], time: number, targetDoc?: Doc) {
+ public static updateKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], time: number, targetDoc?: Doc) {
+ if (timer) clearTimeout(timer);
+ const newTimer = DocumentView.SetViewTransition(docs, 'all', 1000, undefined, true);
const timecode = Math.round(time);
- 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
- );
+ docs.forEach(doc => {
+ 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);
+ });
+ });
+ return newTimer;
}
- 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 gotoKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], duration = 1000) {
+ if (timer) clearTimeout(timer);
+ return DocumentView.SetViewTransition(docs, 'all', duration, undefined, true);
}
public static setupZoom(doc: Doc, targDoc: Doc) {
@@ -203,7 +184,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, OpenWhere.inParentFromScreen);
} else {
const spt = this.screenToLocalTransform().inverse().transformPoint(0, 0);
const fpt = screenXf.transformPoint(spt[0], spt[1]);
@@ -238,7 +219,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
<div
className={'collectionFreeFormDocumentView-container'}
style={{
- outline: this.Highlight ? 'orange solid 2px' : '',
width: this.panelWidth(),
height: this.panelHeight(),
transform: this.transform,
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index d74da9748..dd03b9b99 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -103,7 +103,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
<>
<DocumentView
ref={r => {
- whichDoc !== targetDoc && r?.focus(whichDoc, {});
+ whichDoc !== targetDoc && r?.focus(whichDoc, { instant: true });
}}
{...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit}
isContentActive={returnFalse}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 6d6609317..569579996 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,4 +1,4 @@
-import { computed } from 'mobx';
+import { computed, trace } from 'mobx';
import { observer } from 'mobx-react';
import { AclPrivate, Doc, Opt } from '../../../fields/Doc';
import { ScriptField } from '../../../fields/ScriptField';
@@ -59,7 +59,7 @@ class ObserverJsxParser1 extends JsxParser {
}
}
-const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
+export const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
interface HTMLtagProps {
Document: Doc;
diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss
index 0f3eb14bc..6da0b73ba 100644
--- a/src/client/views/nodes/DocumentLinksButton.scss
+++ b/src/client/views/nodes/DocumentLinksButton.scss
@@ -1,8 +1,9 @@
-@import "../global/globalCssVariables.scss";
+@import '../global/globalCssVariables.scss';
.documentLinksButton-wrapper {
transform-origin: top left;
width: 100%;
+ height: 100%;
}
.documentLinksButton-menu {
@@ -21,6 +22,16 @@
position: absolute;
}
+.documentLinksButton-showCount {
+ position: absolute;
+ border-radius: 50%;
+ opacity: 0.9;
+ pointer-events: auto;
+ display: flex;
+ align-items: center;
+ background-color: $light-blue;
+ color: black;
+}
.documentLinksButton,
.documentLinksButton-endLink,
.documentLinksButton-startLink {
@@ -34,6 +45,7 @@
text-transform: uppercase;
letter-spacing: 2px;
font-size: 10px;
+ transform-origin: top left;
transition: transform 0.2s;
text-align: center;
display: flex;
@@ -46,13 +58,10 @@
cursor: pointer;
}
}
-
.documentLinksButton {
background-color: $dark-gray;
color: $white;
font-weight: bold;
- width: 80%;
- height: 80%;
font-size: 100%;
font-family: 'Roboto';
transition: 0.2s ease all;
@@ -61,31 +70,20 @@
background-color: $black;
}
}
-
.documentLinksButton.startLink {
background-color: $medium-blue;
+ width: 75%;
+ height: 75%;
color: $white;
font-weight: bold;
- width: 80%;
- height: 80%;
font-size: 100%;
transition: 0.2s ease all;
-
- &:hover {
- background-color: $black;
- }
}
.documentLinksButton-endLink {
border: $medium-blue 2px dashed;
color: $medium-blue;
background-color: none !important;
- width: 80%;
- height: 80%;
font-size: 100%;
transition: 0.2s ease all;
-
- &:hover {
- background-color: $light-blue;
- }
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 9ffbf8e37..6f3152981 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -4,19 +4,18 @@ 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, StopEvent } 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 _ = require('lodash');
const higflyout = require('@hig/flyout');
export const { anchorPoints } = higflyout;
@@ -24,10 +23,12 @@ export const Flyout = higflyout.default;
interface DocumentLinksButtonProps {
View: DocumentView;
- Offset?: (number | undefined)[];
+ Bottom?: boolean;
AlwaysOn?: boolean;
InMenu?: boolean;
+ OnHover?: boolean;
StartLink?: boolean; //whether the link HAS been started (i.e. now needs to be completed)
+ ShowCount?: boolean;
scaling?: () => number; // how uch doc is scaled so that link buttons can invert it
}
@observer
@@ -39,9 +40,6 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
@observable public static AnnotationUri: string | undefined;
@observable public static LinkEditorDocView: DocumentView | undefined;
- @observable public static invisibleWebDoc: Opt<Doc>;
- public static invisibleWebRef = React.createRef<HTMLDivElement>();
-
@action
@undoBatch
onLinkButtonMoved = (e: PointerEvent) => {
@@ -119,7 +117,6 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
DocumentLinksButton.StartLink = this.props.View.props.Document;
DocumentLinksButton.StartLinkView = this.props.View;
}
- //action(() => Doc.BrushDoc(this.props.View.Document));
}
};
@@ -256,28 +253,32 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
* 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 btnDim = 30;
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',
- width: btnDim,
- height: btnDim,
- }}>
- {Array.from(this.filteredLinks).length}
- </div>
+ const scaling = Math.min(1, this.props.scaling?.() || 1);
+ const showLinkCount = (onHover?: boolean, offset?: boolean) => (
+ <div
+ className="documentLinksButton-showCount"
+ onPointerDown={this.onLinkMenuOpen}
+ style={{
+ fontSize: (onHover ? btnDim / 2 : 20) * scaling,
+ width: (onHover ? btnDim / 2 : btnDim) * scaling,
+ height: (onHover ? btnDim / 2 : btnDim) * scaling,
+ bottom: offset ? 5 * scaling : onHover ? (-btnDim / 2) * scaling : undefined,
+ }}>
+ <span style={{ width: '100%', display: 'inline-block', textAlign: 'center' }}>{Array.from(this.filteredLinks).length}</span>
</div>
+ );
+ return this.props.ShowCount ? (
+ showLinkCount(this.props.OnHover, this.props.Bottom)
) : (
<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
+ {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 ? StopEvent : this.onLinkButtonDown} onClick={isActive ? this.clearLinks : this.onLinkClick}>
+ <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
+ </div>
+ ) : null}
+ {!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}
@@ -286,34 +287,25 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
<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 ? StopEvent : this.onLinkButtonDown} onClick={isActive ? this.clearLinks : this.onLinkClick}>
- <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
- </div>
- ) : null}
</div>
);
}
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 title = this.props.InMenu ? menuTitle : buttonTitle;
+ const title = this.props.ShowCount ? buttonTitle : menuTitle;
//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"
style={{
- transform: `scale(${this.props.scaling?.() || 1})`,
+ position: this.props.InMenu ? 'relative' : 'absolute',
+ top: 0,
+ pointerEvents: 'none',
}}>
- {(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
- )}
+ <Tooltip title={<div className="dash-tooltip">{title}</div>}>{this.linkButtonInner}</Tooltip>
</div>
);
}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 6cadeec41..453bdac8e 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -28,7 +28,7 @@
transition: outline 0.3s linear;
// background: $white; //overflow: hidden;
- transform-origin: left top;
+ transform-origin: center;
&.minimized {
width: 30px;
@@ -51,6 +51,22 @@
height: calc(100% - 20px);
}
+ .documentView-htmlOverlay {
+ position: absolute;
+ display: flex;
+ top: 0;
+ height: 100%;
+ width: 100%;
+ .documentView-htmlOverlayInner {
+ box-shadow: black 0.2vw 0.2vw 0.8vw;
+ background: rgb(255, 255, 255);
+ overflow: auto;
+ position: relative;
+ margin: auto;
+ padding: 20px;
+ }
+ }
+
.documentView-linkAnchorBoxAnchor {
display: flex;
overflow: hidden;
@@ -198,9 +214,9 @@
> .documentView-titleWrapper-hover {
display: inline-block;
}
- }
-
- > .documentView-styleWrapper {
+ // > .documentView-contentsView {
+ // opacity: 0.5;
+ // }
> .documentView-captionWrapper {
opacity: 1;
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5b26469ed..1df46488b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -3,7 +3,9 @@ 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, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
+import { ObserverJsxParser } from './DocumentContentsView';
+import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
+import { AclAdmin, AclEdit, AclPrivate, AnimationSym, 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';
@@ -11,7 +13,7 @@ import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
import { listSpec } from '../../../fields/Schema';
import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { AudioField } from '../../../fields/URLField';
import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
import { MobileInterface } from '../../../mobile/MobileInterface';
@@ -51,9 +53,9 @@ import { LinkAnchorBox } from './LinkAnchorBox';
import { LinkDocPreview } from './LinkDocPreview';
import { RadialMenu } from './RadialMenu';
import { ScriptingBox } from './ScriptingBox';
-import { PresBox } from './trails/PresBox';
+import { PresEffect, PresEffectDirection } from './trails';
+import { PinProps } from './trails/PresBox';
import React = require('react');
-import { CollectionTreeView } from '../collections/CollectionTreeView';
const { Howl } = require('howler');
interface Window {
@@ -70,15 +72,49 @@ export enum ViewAdjustment {
doNothing = 0,
}
+export enum OpenWhere {
+ inPlace = 'inPlace',
+ lightbox = 'lightbox',
+ add = 'add',
+ addLeft = 'add:left',
+ addRight = 'add:right',
+ addBottom = 'add:bottom',
+ dashboard = 'dashboard',
+ close = 'close',
+ fullScreen = 'fullScreen',
+ toggle = 'toggle',
+ replace = 'replace',
+ replaceRight = 'replace:right',
+ replaceLeft = 'replace:left',
+ inParent = 'inParent',
+ inParentFromScreen = 'inParentFromScreen',
+}
+export enum OpenWhereMod {
+ none = '',
+ left = 'left',
+ right = 'right',
+ top = 'top',
+ bottom = 'bottom',
+}
+
export const ViewSpecPrefix = 'viewSpec'; // field prefix for anchor fields that are immediately copied over to the target document when link is followed. Other anchor properties will be copied over in the specific setViewSpec() method on their view (which allows for seting preview values instead of writing to the document)
export interface DocFocusOptions {
originalTarget?: Doc; // set in JumpToDocument, used by TabDocView to determine whether to fit contents to tab
- willZoom?: boolean; // determines whether to zoom in on target document
- scale?: number; // percent of containing frame to zoom into document
+ willPan?: boolean; // determines whether to pan to target document
+ willPanZoom?: boolean; // determines whether to zoom in on target document
+ zoomScale?: number; // percent of containing frame to zoom into document
+ zoomTime?: number;
afterFocus?: DocAfterFocusFunc; // function to call after focusing on a document
docTransform?: Transform; // when a document can't be panned and zoomed within its own container (say a group), then we need to continue to move up the render hierarchy to find something that can pan and zoom. when this happens the docTransform must accumulate all the transforms of each level of the hierarchy
instant?: boolean; // whether focus should happen instantly (as opposed to smooth zoom)
+ effect?: Doc; // animation effect for focus
+ noSelect?: boolean; // whether target should be selected after focusing
+ playAudio?: boolean; // whether to play audio annotation on focus
+ zoomTextSelections?: boolean; // whether to display a zoomed overlay of anchor text selections
+ toggleTarget?: boolean; // whether to toggle target on and off
+ originatingDoc?: Doc; // document that triggered the focus
+ easeFunc?: 'linear' | 'ease'; // transition method for scrolling
}
export type DocAfterFocusFunc = (notFocused: boolean) => Promise<ViewAdjustment>;
export type DocFocusFunc = (doc: Doc, options: DocFocusOptions) => void;
@@ -86,7 +122,7 @@ export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, p
export interface DocComponentView {
updateIcon?: () => void; // updates the icon representation of the document
getAnchor?: () => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
- scrollFocus?: (doc: Doc, smooth: boolean) => Opt<number>; // returns the duration of the focus
+ scrollFocus?: (doc: Doc, options: DocFocusOptions) => Opt<number>; // returns the duration of the focus
brushView?: (view: { width: number; height: number; panX: number; panY: number }) => void;
setViewSpec?: (anchor: Doc, preview: boolean) => void; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
@@ -99,6 +135,7 @@ export interface DocComponentView {
Pause?: () => void;
setFocus?: () => void;
componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null;
+ incrementalRendering?: () => void;
fieldKey?: string;
annotationKey?: string;
getTitle?: () => string;
@@ -140,14 +177,15 @@ export interface DocumentViewSharedProps {
showTitle?: () => string;
whenChildContentsActiveChanged: (isActive: boolean) => void;
rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected
- addDocTab: (doc: Doc, where: string) => boolean;
+ addDocTab: (doc: Doc, where: OpenWhere) => boolean;
filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
addDocument?: (doc: Doc | Doc[]) => boolean;
removeDocument?: (doc: Doc | Doc[]) => boolean;
moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
- pinToPres: (document: Doc) => void;
+ pinToPres: (document: Doc, pinProps: PinProps) => void;
ScreenToLocalTransform: () => Transform;
bringToFront: (doc: Doc, sendToBack?: boolean) => void;
+ canEmbedOnDrag?: boolean;
xPadding?: number;
yPadding?: number;
dropAction?: dropActionType;
@@ -167,7 +205,8 @@ export interface DocumentViewSharedProps {
// these props are specific to DocuentViews
export interface DocumentViewProps extends DocumentViewSharedProps {
// properties specific to DocumentViews but not to FieldView
- hideResizeHandles?: boolean; // whether to suppress DocumentDecorations when this document is selected
+ hideDecorations?: boolean; // whether to suppress all DocumentDecorations when doc is selected
+ hideResizeHandles?: boolean; // whether to suppress resized handles on doc decorations when this document is selected
hideTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
hideDecorationTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
hideDocumentButtonBar?: boolean;
@@ -199,6 +238,7 @@ export interface DocumentViewInternalProps extends DocumentViewProps {
NativeWidth: () => number;
NativeHeight: () => number;
isSelected: (outsideReaction?: boolean) => boolean;
+ isHovering: () => boolean;
select: (ctrlPressed: boolean) => void;
DocumentView: () => DocumentView;
viewPath: () => DocumentView[];
@@ -207,9 +247,8 @@ export interface DocumentViewInternalProps extends DocumentViewProps {
@observer
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 _pendingDoubleClick = false;
+ private _cursorTimer: NodeJS.Timeout | undefined;
+ private _longPress = false;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _downX: number = 0;
private _downY: number = 0;
@@ -223,11 +262,19 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
private _dropDisposer?: DragManager.DragDropDisposer;
private _holdDisposer?: InteractionUtils.MultiTouchEventDisposer;
protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
+
@observable _componentView: Opt<DocComponentView>; // needs to be accessed from DocumentView wrapper class
+ @observable _animateScaleTime: Opt<number>; // milliseconds for animating between views. defaults to 300 if not uset
+ @observable _animateScalingTo = 0;
+ @observable _pendingDoubleClick = false;
+ @observable _cursorPress = false;
private get topMost() {
return this.props.renderDepth === 0 && !LightboxView.LightboxDoc;
}
+ public get animateScaleTime() {
+ return this._animateScaleTime ?? 300;
+ }
public get displayName() {
return 'DocumentView(' + this.props.Document.title + ')';
} // this makes mobx trace() statements more descriptive
@@ -258,9 +305,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@computed get borderRounding() {
return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
}
- @computed get hideLinkButton() {
- return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkButton + (this.props.isSelected() ? ':selected' : ''));
- }
@computed get widgetDecorations() {
return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Decorations + (this.props.isSelected() ? ':selected' : ''));
}
@@ -475,7 +519,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const pt = me.touchEvent.touches[me.touchEvent.touches.length - 1];
RadialMenu.Instance.openMenu(pt.pageX - 15, pt.pageY - 15);
- // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "map-pin", selected: -1 });
+ // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), OpenWhere.addRight), icon: "map-pin", selected: -1 });
const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]);
(effectiveAcl === AclEdit || effectiveAcl === AclAdmin) &&
RadialMenu.Instance.addItem({
@@ -486,8 +530,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
icon: 'external-link-square-alt',
selected: -1,
});
- // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "add:right"), icon: "trash", selected: -1 });
- RadialMenu.Instance.addItem({ description: 'Pin', event: () => this.props.pinToPres(this.props.Document), icon: 'map-pin', selected: -1 });
+ // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, OpenWhere.addRight), icon: "trash", selected: -1 });
+ RadialMenu.Instance.addItem({ description: 'Pin', event: () => this.props.pinToPres(this.props.Document, {}), icon: 'map-pin', selected: -1 });
RadialMenu.Instance.addItem({ description: 'Open', event: () => MobileInterface.Instance.handleClick(this.props.Document), icon: 'trash', selected: -1 });
SelectionManager.DeselectAll();
@@ -507,6 +551,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
dragData.treeViewDoc = this.props.treeViewDoc;
dragData.removeDocument = this.props.removeDocument;
dragData.moveDocument = this.props.moveDocument;
+ dragData.canEmbed = this.props.canEmbedOnDrag;
//dragData.dimSource :
// dragEffects field, set dim
// add kv pairs to a doc, swap properties with the node while dragging, and then swap when dropping
@@ -527,7 +572,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
e.preventDefault();
if (e.key === '†' || e.key === 't') {
if (!StrCast(this.layoutDoc._showTitle)) this.layoutDoc._showTitle = 'title';
- if (!this._titleRef.current) setTimeout(() => this._titleRef.current?.setIsFocused(true), 0);
+ if (!this._titleRef.current) setTimeout(() => this._titleRef.current?.setIsFocused(true));
else if (!this._titleRef.current.setIsFocused(true)) {
// if focus didn't change, focus on interior text...
this._titleRef.current?.setIsFocused(false);
@@ -542,16 +587,21 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
// copying over VIEW fields immediately allows the view type to switch to create the right _componentView
Array.from(Object.keys(Doc.GetProto(anchor)))
.filter(key => key.startsWith(ViewSpecPrefix))
- .forEach(spec => {
- this.layoutDoc[spec.replace(ViewSpecPrefix, '')] = (field => (field instanceof ObjectField ? ObjectField.MakeCopy(field) : field))(anchor[spec]);
- });
- // after a timeout, the right _componentView should have been created, so call it to update its view spec values
+ .forEach(spec => (this.layoutDoc[spec.replace(ViewSpecPrefix, '')] = (field => (field instanceof ObjectField ? ObjectField.MakeCopy(field) : field))(anchor[spec])));
+ // after a render the general viewSpec should have created the right _componentView, so after a timeout, call the componentview to update its specific view specs
setTimeout(() => this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false));
- const focusSpeed = this._componentView?.scrollFocus?.(anchor, options?.instant === false || !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here
+ const focusSpeed = this._componentView?.scrollFocus?.(anchor, { ...options, instant: options?.instant || LinkDocPreview.LinkInfo ? true : false });
const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus?.(true) ?? ViewAdjustment.doNothing;
+ const startTime = Date.now();
this.props.focus(options?.docTransform ? anchor : this.rootDoc, {
...options,
- afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(endFocus ? await endFocus(didFocus || focusSpeed !== undefined) : ViewAdjustment.doNothing), focusSpeed ?? 0)),
+ afterFocus: (didFocus: boolean) =>
+ new Promise<ViewAdjustment>(async res =>
+ setTimeout(
+ async () => res(endFocus ? await endFocus(didFocus || focusSpeed !== undefined) : ViewAdjustment.doNothing), //
+ didFocus ? Math.max(0, (options.zoomTime ?? 500) - (Date.now() - startTime)) : 0
+ )
+ ),
});
};
onClick = action((e: React.MouseEvent | React.PointerEvent) => {
@@ -588,11 +638,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
);
UndoManager.RunInBatch(() => (func().result?.select === true ? this.props.select(false) : ''), 'on double click');
} else if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.isLinkButton) {
- UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, 'lightbox', this.props.LayoutTemplate?.(), this.props.addDocTab), 'double tap');
+ UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, OpenWhere.lightbox, this.props.LayoutTemplate?.(), this.props.addDocTab), 'double tap');
SelectionManager.DeselectAll();
Doc.UnBrushDoc(this.props.Document);
}
- } else if (this.onClickHandler?.script && !isScriptBox()) {
+ } else if (!this._longPress && this.onClickHandler?.script && !isScriptBox()) {
// bcz: hack? don't execute script if you're clicking on a scripting box itself
const { clientX, clientY, shiftKey, altKey } = e;
const func = () =>
@@ -621,11 +671,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
clickFunc();
}, 350);
} else clickFunc();
- } else if (this.allLinks && this.Document.type !== DocumentType.LINK && !isScriptBox() && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
+ } else if (!this._longPress && this.allLinks.length && this.Document.type !== DocumentType.LINK && !isScriptBox() && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
+ SelectionManager.DeselectAll();
this.allLinks.length && LinkFollower.FollowLink(undefined, this.props.Document, this.props, e.altKey);
} else {
if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {
- // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
+ // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
} else {
runInAction(() => (this._pendingDoubleClick = true));
@@ -656,6 +707,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
return;
}
+ this._cursorTimer = setTimeout(
+ action(() => {
+ this._cursorPress = true;
+ this.props.select(false);
+ }),
+ 1000 // long press required duration
+ );
this._downX = e.clientX;
this._downY = e.clientY;
if ((Doc.ActiveTool === InkTool.None || this.props.addDocTab === returnFalse) && !(this.props.Document.rootDocument && !(e.ctrlKey || e.button > 0))) {
@@ -681,6 +739,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
};
+ @action
onPointerMove = (e: PointerEvent): void => {
if (e.cancelBubble) return;
if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) return;
@@ -690,7 +749,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
document.removeEventListener('pointermove', this.onPointerMove);
document.removeEventListener('pointerup', this.onPointerUp);
- this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'alias') || ((this.props.dropAction || this.Document.dropAction || undefined) as dropActionType));
+ this._cursorTimer && clearTimeout(this._cursorTimer);
+ this._cursorPress = false;
+ this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'alias') || ((this.Document.dropAction || this.props.dropAction || undefined) as dropActionType));
}
}
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
@@ -704,8 +765,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
document.removeEventListener('pointerup', this.onPointerUp);
};
+ @action
onPointerUp = (e: PointerEvent): void => {
this.cleanupPointerEvents();
+ this._longPress = this._cursorPress;
+ this._cursorTimer && clearTimeout(this._cursorTimer);
+ this._cursorPress = false;
if (this.onPointerUpHandler?.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
this.onPointerUpHandler.script.run({ self: this.rootDoc, this: this.layoutDoc }, console.log);
@@ -721,8 +786,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
toggleFollowLink = (location: Opt<string>, zoom?: boolean, setPushpin?: boolean): void => {
this.Document.ignoreClick = false;
if (setPushpin) {
- this.Document.isPushpin = !this.Document.isPushpin;
- this.Document._isLinkButton = this.Document.isPushpin || this.Document._isLinkButton;
+ this.Document.followLinkToggle = !this.Document.followLinkToggle;
+ this.Document._isLinkButton = this.Document.followLinkToggle || this.Document._isLinkButton;
} else {
this.Document._isLinkButton = !this.Document._isLinkButton;
}
@@ -739,14 +804,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
toggleTargetOnClick = (): void => {
this.Document.ignoreClick = false;
this.Document._isLinkButton = true;
- this.Document.isPushpin = true;
+ this.Document.followLinkToggle = true;
};
@undoBatch
@action
followLinkOnClick = (location: Opt<string>, zoom: boolean): void => {
this.Document.ignoreClick = false;
this.Document._isLinkButton = true;
- this.Document.isPushpin = false;
+ this.Document.followLinkToggle = false;
this.Document.followLinkZoom = zoom;
this.Document.followLinkLocation = location;
};
@@ -755,7 +820,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
selectOnClick = (): void => {
this.Document.ignoreClick = false;
this.Document._isLinkButton = false;
- this.Document.isPushpin = false;
+ this.Document.followLinkToggle = false;
this.Document.onClick = this.layoutDoc.onClick = undefined;
};
@undoBatch
@@ -803,7 +868,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' });
DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, 'portal to:portal from');
}
- this.Document.followLinkLocation = 'inPlace';
+ this.Document.followLinkLocation = OpenWhere.inPlace;
this.Document.followLinkZoom = true;
this.Document._isLinkButton = true;
};
@@ -834,14 +899,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
if (!cm || (e as any)?.nativeEvent?.SchemaHandled) return;
if (e && !(e.nativeEvent as any).dash) {
- const onDisplay = () =>
- setTimeout(() => {
- DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up. by delaying the selection until hopefully after the pointer up, the context menu will appear.
- setTimeout(() => {
- const ele = document.elementFromPoint(e.clientX, e.clientY);
- simulateMouseClick(ele, e.clientX, e.clientY, e.screenX, e.screenY);
- });
- });
+ const onDisplay = () => {
+ DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up. by delaying the selection until hopefully after the pointer up, the context menu will appear.
+ setTimeout(() => simulateMouseClick(document.elementFromPoint(e.clientX, e.clientY), e.clientX, e.clientY, e.screenX, e.screenY));
+ };
if (navigator.userAgent.includes('Macintosh')) {
cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15, undefined, undefined, onDisplay);
} else {
@@ -862,7 +923,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
const appearance = cm.findByDescription('UI Controls...');
const appearanceItems: ContextMenuProps[] = appearance && 'subitems' in appearance ? appearance.subitems : [];
- !Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template ', event: () => this.props.addDocTab(templateDoc, 'add:right'), icon: 'eye' });
+ !Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template ', event: () => this.props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' });
!Doc.noviceMode &&
appearanceItems.push({
description: 'Add a Field',
@@ -915,10 +976,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
!options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' });
onClicks.push({ description: this.Document.ignoreClick ? 'Select' : 'Do Nothing', event: () => (this.Document.ignoreClick = !this.Document.ignoreClick), icon: this.Document.ignoreClick ? 'unlock' : 'lock' });
- onClicks.push({ description: this.Document.isLinkButton ? 'Remove Follow Behavior' : 'Follow Link in Place', event: () => this.toggleFollowLink('inPlace', true, false), icon: 'link' });
+ onClicks.push({ description: this.Document.isLinkButton ? 'Remove Follow Behavior' : 'Follow Link in Place', event: () => this.toggleFollowLink('inPlace', false, false), icon: 'link' });
!this.Document.isLinkButton && onClicks.push({ description: 'Follow Link on Right', event: () => this.toggleFollowLink('add:right', false, false), icon: 'link' });
onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? 'Remove Click Behavior' : 'Follow Link', event: () => this.toggleFollowLink(undefined, false, false), icon: 'link' });
- onClicks.push({ description: (this.Document.isPushpin ? 'Remove' : 'Make') + ' Pushpin', event: () => this.toggleFollowLink(undefined, false, true), icon: 'map-pin' });
+ onClicks.push({ description: (this.Document.followLinkToggle ? 'Remove' : 'Make') + ' Pushpin', event: () => this.toggleFollowLink(undefined, false, true), icon: 'map-pin' });
onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' });
!existingOnClick && cm.addItem({ description: 'OnClick...', addDivider: true, noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
} else if (DocListCast(this.Document.links).length) {
@@ -962,8 +1023,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
const help = cm.findByDescription('Help...');
const helpItems: ContextMenuProps[] = help && 'subitems' in help ? help.subitems : [];
- helpItems.push({ description: 'Show Metadata', event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), 'add:right'), icon: 'layer-group' });
- !Doc.noviceMode && helpItems.push({ description: 'Text Shortcuts Ctrl+/', event: () => this.props.addDocTab(Docs.Create.PdfDocument('/assets/cheat-sheet.pdf', { _width: 300, _height: 300 }), 'add:right'), icon: 'keyboard' });
+ helpItems.push({ description: 'Show Metadata', event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), OpenWhere.addRight), icon: 'layer-group' });
+ !Doc.noviceMode && helpItems.push({ description: 'Text Shortcuts Ctrl+/', event: () => this.props.addDocTab(Docs.Create.PdfDocument('/assets/cheat-sheet.pdf', { _width: 300, _height: 300 }), OpenWhere.addRight), icon: 'keyboard' });
!Doc.noviceMode && helpItems.push({ description: 'Print Document in Console', event: () => console.log(this.props.Document), icon: 'hand-point-right' });
!Doc.noviceMode && helpItems.push({ description: 'Print DataDoc in Console', event: () => console.log(this.props.Document[DataSym]), icon: 'hand-point-right' });
@@ -1046,34 +1107,41 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
};
@observable _retryThumb = 1;
thumbShown = () => {
+ const childHighlighted = () =>
+ Array.from(Doc.highlightedDocs.keys())
+ .concat(Array.from(Doc.brushManager.BrushedDoc.keys()))
+ .some(doc => Doc.AreProtosEqual(DocCast(doc.annotationOn), this.rootDoc));
+ const childOverlayed = () => Array.from(DocumentManager._overlayViews).some(view => Doc.AreProtosEqual(view.rootDoc, this.rootDoc));
return !this.props.isSelected() &&
LightboxView.LightboxDoc !== this.rootDoc &&
this.thumb &&
!Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) &&
- (!Doc.isBrushedHighlightedDegree(this.props.Document) || this.rootDoc._viewType === CollectionViewType.Docking) &&
+ ((!childHighlighted() && !childOverlayed() && !Doc.isBrushedHighlightedDegree(this.rootDoc)) || this.rootDoc._viewType === CollectionViewType.Docking) &&
!this._componentView?.isAnyChildContentActive?.()
? true
: false;
};
- linkButtonInverseScaling = () => (this.props.NativeDimScaling?.() || 1) * this.props.DocumentView().screenToLocalTransform().Scale;
- @computed get contents() {
- TraceMobx();
+ get audioAnnoState() {
+ return this.dataDoc.audioAnnoState ?? 'stopped';
+ }
+ @computed get audioAnnoView() {
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>
- );
-
+ const audioIconColors = new Map<string, string>([
+ ['recording', 'red'],
+ ['playing', 'green'],
+ ['stopped', audioAnnosCount ? 'blue' : 'gray'],
+ ]);
+ return this.props.renderDepth === -1 || SnappingManager.GetIsDragging() || (!this.props.isSelected() && !this.props.isHovering() && this.audioAnnoState !== 'recording') || (!audioAnnosCount && this.audioAnnoState === 'stopped') ? null : (
+ <Tooltip title={<div>{audioTextAnnos?.lastElement()}</div>}>
+ <div className="documentView-audioBackground" onPointerDown={this.playAnnotation}>
+ <FontAwesomeIcon className="documentView-audioFont" style={{ color: audioIconColors.get(StrCast(this.audioAnnoState)) }} icon={!audioAnnosCount ? 'microphone' : 'file-audio'} size="sm" />
+ </div>
+ </Tooltip>
+ );
+ }
+ @computed get contents() {
+ TraceMobx();
return (
<div
className="documentView-contentsView"
@@ -1095,10 +1163,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
width={this.props.PanelWidth()}
height={this.props.PanelHeight()}
onError={(e: any) => {
- setTimeout(
- action(() => (this._retryThumb = 0)),
- 0
- );
+ setTimeout(action(() => (this._retryThumb = 0)));
setTimeout(
action(() => (this._retryThumb = 1)),
150
@@ -1111,7 +1176,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
{...this.props}
docViewPath={this.props.viewPath}
thumbShown={this.thumbShown}
- isHovering={this.isHovering}
+ isHovering={this.props.isHovering}
setContentView={this.setContentView}
NativeDimScaling={this.props.NativeDimScaling}
PanelHeight={this.panelHeight}
@@ -1124,14 +1189,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
layoutKey={this.finalLayoutKey}
/>
{this.layoutDoc.hideAllLinks ? null : this.allLinkEndpoints}
- {(!this.props.isSelected() && !this._isHovering) || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? null : (
- <DocumentLinksButton
- View={this.props.DocumentView()}
- scaling={this.linkButtonInverseScaling}
- Offset={[this.topMost ? 0 : !this.props.isSelected() ? -15 : -36, undefined, undefined, this.topMost ? 10 : !this.props.isSelected() ? -15 : -32]}
- />
- )}
- {audioView}
+ {this.audioAnnoView}
</div>
);
}
@@ -1147,16 +1205,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
anchorPanelWidth = () => this.props.PanelWidth() || 1;
anchorPanelHeight = () => this.props.PanelHeight() || 1;
anchorStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
- switch (property) {
- case StyleProp.ShowTitle:
- return '';
- case StyleProp.PointerEvents:
- return 'none';
- case StyleProp.LinkSource:
- return this.props.Document; // pass the LinkSource to the LinkAnchorBox
- default:
- return this.props.styleProvider?.(doc, props, property);
+ // prettier-ignore
+ switch (property.split(':')[0]) {
+ case StyleProp.ShowTitle: return '';
+ case StyleProp.PointerEvents: return 'none';
+ case StyleProp.LinkSource: return this.props.Document; // pass the LinkSource to the LinkAnchorBox
}
+ return this.props.styleProvider?.(doc, props, property);
};
// We need to use allrelatedLinks to get not just links to the document as a whole, but links to
// anchors that are not rendered as DocumentViews (marked as 'unrendered' with their 'annotationOn' set to this document). e.g.,
@@ -1183,7 +1238,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
TraceMobx();
if (this.layoutDoc.unrendered || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return null;
if (this.rootDoc.type === DocumentType.PRES || this.rootDoc.type === DocumentType.LINK || this.props.dontRegisterView) return null;
- const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => !d.hidden);
+ const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => d.linkDisplay);
return filtered.map((link, i) => (
<div className="documentView-anchorCont" key={link[Id]}>
<DocumentView
@@ -1210,7 +1265,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const self = this;
const audioAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations'], listSpec(AudioField), null);
const anno = audioAnnos.lastElement();
- if (anno instanceof AudioField && this.dataDoc.audioAnnoState === 0) {
+ if (anno instanceof AudioField && this.audioAnnoState === 'stopped') {
new Howl({
src: [anno.url.href],
format: ['mp3'],
@@ -1218,16 +1273,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
loop: false,
volume: 0.5,
onend: function () {
- runInAction(() => {
- self.dataDoc.audioAnnoState = 0;
- });
+ runInAction(() => (self.dataDoc.audioAnnoState = 'stopped'));
},
});
- this.dataDoc.audioAnnoState = 1;
+ this.dataDoc.audioAnnoState = 'playing';
}
};
- static recordAudioAnnotation(dataDoc: Doc, field: string, onEnd?: () => void) {
+ static recordAudioAnnotation(dataDoc: Doc, field: string, onRecording?: (stop: () => void) => void, onEnd?: () => void) {
let gumStream: any;
let recorder: any;
navigator.mediaDevices
@@ -1262,14 +1315,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
}
};
- runInAction(() => (dataDoc.audioAnnoState = 2));
+ runInAction(() => (dataDoc.audioAnnoState = 'recording'));
recorder.start();
- setTimeout(() => {
+ const stopFunc = () => {
recorder.stop();
DictationManager.Controls.stop(false);
- runInAction(() => (dataDoc.audioAnnoState = 0));
+ runInAction(() => (dataDoc.audioAnnoState = 'stopped'));
gumStream.getAudioTracks()[0].stop();
- }, 5000);
+ };
+ if (onRecording) onRecording(stopFunc);
+ else setTimeout(stopFunc, 5000);
});
}
@@ -1329,7 +1384,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
.join('\\')}
display={'block'}
fontSize={10}
- GetValue={() => (showTitle.split(';').length === 1 ? showTitle + '=' + Field.toString(targetDoc[showTitle.split(';')[0]] as any as Field) : '#' + showTitle)}
+ GetValue={() => {
+ this.props.select(false);
+ return showTitle.split(';').length === 1 ? showTitle + '=' + Field.toString(targetDoc[showTitle.split(';')[0]] as any as Field) : '#' + showTitle;
+ }}
SetValue={undoBatch((input: string) => {
if (input?.startsWith('#')) {
if (this.props.showTitle) {
@@ -1360,10 +1418,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
</div>
);
}
- isHovering = () => this._isHovering;
- @observable _isHovering = false;
@observable _: string = '';
- _hoverTimeout: any = undefined;
renderDoc = (style: object) => {
TraceMobx();
const thumb = ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url?.href.replace('.png', '_m.png');
@@ -1374,27 +1429,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
<div
className={`documentView-node${this.topMost ? '-topmost' : ''}`}
id={this.props.Document[Id]}
- onPointerEnter={action(() => {
- clearTimeout(this._hoverTimeout);
- this._isHovering = true;
- })}
- onPointerLeave={action(() => {
- clearTimeout(this._hoverTimeout);
- this._hoverTimeout = setTimeout(
- action(() => (this._isHovering = false)),
- 500
- );
- })}
style={{
...style,
background: isButton || thumb ? undefined : this.backgroundColor,
opacity: this.opacity,
- cursor: Doc.ActiveTool === InkTool.None ? 'grab' : 'crosshair',
+ cursor: this._cursorPress ? 'wait' : 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),
transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
- transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform ${this._animateScaleTime / 1000}s ease-${this._animateScalingTo < 1 ? 'in' : 'out'}`,
+ transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform ${this.animateScaleTime / 1000}s ease-${this._animateScalingTo < 1 ? 'in' : 'out'}`,
}}>
{this.innards}
{this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <div className="documentView-contentBlocker" /> : null}
@@ -1403,25 +1447,55 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
)
);
};
+
+ /**
+ * returns an entrance animation effect function to wrap a JSX element
+ * @param presEffectDoc presentation effects document that specifies the animation effect parameters
+ * @returns a function that will wrap a JSX animation element wrapping any JSX element
+ */
+ public static AnimationEffect(renderDoc: JSX.Element, presEffectDoc: Opt<Doc>, root: Doc) {
+ const dir = presEffectDoc?.presEffectDirection ?? presEffectDoc?.linkAnimDirection;
+ const effectProps = {
+ left: dir === PresEffectDirection.Left,
+ right: dir === PresEffectDirection.Right,
+ top: dir === PresEffectDirection.Top,
+ bottom: dir === PresEffectDirection.Bottom,
+ opposite: true,
+ delay: 0,
+ duration: Cast(presEffectDoc?.presTransition, 'number', null),
+ };
+ //prettier-ignore
+ switch (StrCast(presEffectDoc?.presEffect, StrCast(presEffectDoc?.followLinkAnimEffect))) {
+ 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>;
+ }
+ }
render() {
TraceMobx();
const highlighting = this.props.styleProvider?.(this.props.Document, this.props, StyleProp.Highlighting);
const borderPath = this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BorderPath) || { path: undefined };
const boxShadow =
this.props.treeViewDoc || !highlighting
- ? null
+ ? this.boxShadow
: highlighting && this.borderRounding && highlighting.highlightStyle !== 'dashed'
? `0 0 0 ${highlighting.highlightIndex}px ${highlighting.highlightColor}`
: this.boxShadow || (this.props.Document.isTemplateForField ? 'black 0.2vw 0.2vw 0.8vw' : undefined);
const renderDoc = this.renderDoc({
borderRadius: this.borderRounding,
- outline: highlighting && !this.borderRounding && highlighting ? `${highlighting.highlightColor} ${highlighting.highlightStyle} ${highlighting.highlightIndex}px` : 'solid 0px',
- border: highlighting && this.borderRounding && highlighting && highlighting.highlightStyle === 'dashed' ? `${highlighting.highlightStyle} ${highlighting.highlightColor} ${highlighting.highlightIndex}px` : undefined,
+ outline: highlighting && !this.borderRounding && !highlighting.highlightStroke ? `${highlighting.highlightColor} ${highlighting.highlightStyle} ${highlighting.highlightIndex}px` : 'solid 0px',
+ border: highlighting && this.borderRounding && highlighting.highlightStyle === 'dashed' ? `${highlighting.highlightStyle} ${highlighting.highlightColor} ${highlighting.highlightIndex}px` : undefined,
boxShadow,
clipPath: borderPath.path ? `path('${borderPath.path}')` : undefined,
});
- const animRenderDoc = PresBox.Instance?.isActiveItemTarget(this.layoutDoc) ? PresBox.AnimationEffect(renderDoc, PresBox.Instance.activeItem) : renderDoc;
+ const animRenderDoc = DocumentViewInternal.AnimationEffect(renderDoc, this.rootDoc[AnimationSym], this.rootDoc);
return (
<div
className={`${DocumentView.ROOT_DIV} docView-hack`}
@@ -1474,7 +1548,37 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return 'DocumentView(' + this.props.Document?.title + ')';
} // this makes mobx trace() statements more descriptive
public ContentRef = React.createRef<HTMLDivElement>();
+ public ViewTimer: NodeJS.Timeout | undefined; // timer for res
private _disposers: { [name: string]: IReactionDisposer } = {};
+ public clearViewTransition = () => {
+ this.ViewTimer && clearTimeout(this.ViewTimer);
+ this.rootDoc._viewTransition = undefined;
+ };
+ public setViewTransition = (transProp: string, timeInMs: number, afterTrans?: () => void, dataTrans = false) => {
+ this.rootDoc._viewTransition = `${transProp} ${timeInMs}ms`;
+ if (dataTrans) this.rootDoc._dataTransition = `${transProp} ${timeInMs}ms`;
+ this.ViewTimer && clearTimeout(this.ViewTimer);
+ return (this.ViewTimer = setTimeout(() => {
+ this.rootDoc._viewTransition = undefined;
+ this.rootDoc._dataTransition = 'inherit';
+ afterTrans?.();
+ }, timeInMs + 10));
+ };
+ public static SetViewTransition(docs: Doc[], transProp: string, timeInMs: number, afterTrans?: () => void, dataTrans = false) {
+ docs.forEach(doc => {
+ doc._viewTransition = `${transProp} ${timeInMs}ms`;
+ dataTrans && (doc.dataTransition = `${transProp} ${timeInMs}ms`);
+ });
+ return setTimeout(
+ () =>
+ docs.forEach(doc => {
+ doc._viewTransition = undefined;
+ dataTrans && (doc.dataTransition = 'inherit');
+ afterTrans?.();
+ }),
+ timeInMs + 10
+ );
+ }
public static showBackLinks(linkSource: Doc) {
const docid = Doc.CurrentUserEmail + Doc.GetProto(linkSource)[Id] + '-pivotish';
@@ -1535,6 +1639,16 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return this.props.fitWidth?.(this.rootDoc) || this.layoutDoc.fitWidth;
}
+ @computed get hideLinkButton() {
+ return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkButton + (this.isSelected() ? ':selected' : ''));
+ }
+ linkButtonInverseScaling = () => (this.props.NativeDimScaling?.() || 1) * this.screenToLocalTransform().Scale;
+
+ @computed get linkCountView() {
+ const hideCount = this.props.renderDepth === -1 || SnappingManager.GetIsDragging() || (this.isSelected() && this.props.renderDepth) || !this._isHovering || this.hideLinkButton;
+ return hideCount ? null : <DocumentLinksButton View={this} scaling={this.linkButtonInverseScaling} OnHover={true} Bottom={this.topMost} ShowCount={true} />;
+ }
+
@computed get docViewPath(): DocumentView[] {
return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this];
}
@@ -1576,7 +1690,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return this.props.PanelHeight();
}
@computed get Xshift() {
- return this.effectiveNativeWidth ? (this.props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2 : 0;
+ return this.effectiveNativeWidth ? Math.max(0, (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 || (!this.fitWidth && this.effectiveNativeHeight * this.nativeScaling <= this.props.PanelHeight()))
@@ -1593,10 +1707,13 @@ export class DocumentView extends React.Component<DocumentViewProps> {
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.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
+ if (!this.docView || !this.docView.ContentDiv || this.props.Document.type === DocumentType.PRES || this.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
return undefined;
}
- const xf = this.docView?.props.ScreenToLocalTransform().scale(this.nativeScaling).inverse();
+ const xf = this.docView?.props
+ .ScreenToLocalTransform()
+ .scale(this.trueNativeWidth() ? this.nativeScaling : 1)
+ .inverse();
const [[left, top], [right, bottom]] = [xf.transformPoint(0, 0), xf.transformPoint(this.panelWidth, this.panelHeight)];
if (this.docView.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
const docuBox = this.docView.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
@@ -1605,15 +1722,21 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return { left, top, right, bottom, center: this.ComponentView?.getCenter?.(xf) };
};
- public iconify(finished?: () => void) {
+ public iconify(finished?: () => void, animateTime?: number) {
this.ComponentView?.updateIcon?.();
+ const animTime = this.docView?._animateScaleTime;
+ runInAction(() => this.docView && animateTime !== undefined && (this.docView._animateScaleTime = animateTime));
+ const finalFinished = action(() => {
+ finished?.();
+ this.docView && (this.docView._animateScaleTime = animTime);
+ });
const layoutKey = Cast(this.Document.layoutKey, 'string', null);
if (layoutKey !== 'layout_icon') {
- this.switchViews(true, 'icon', finished);
+ this.switchViews(true, 'icon', finalFinished);
if (layoutKey && layoutKey !== 'layout' && layoutKey !== 'layout_icon') this.Document.deiconifyLayout = layoutKey.replace('layout_', '');
} else {
const deiconifyLayout = Cast(this.Document.deiconifyLayout, 'string', null);
- this.switchViews(deiconifyLayout ? true : false, deiconifyLayout, finished);
+ this.switchViews(deiconifyLayout ? true : false, deiconifyLayout, finalFinished);
this.Document.deiconifyLayout = undefined;
this.props.bringToFront(this.rootDoc);
}
@@ -1639,15 +1762,19 @@ export class DocumentView extends React.Component<DocumentViewProps> {
this.docView && (this.docView._animateScalingTo = 0);
finished?.();
}),
- this.docView!._animateScaleTime - 10
+ this.docView ? Math.max(0, this.docView.animateScaleTime - 10) : 0
);
}),
- this.docView!._animateScaleTime - 10
+ this.docView ? Math.max(0, this.docView?.animateScaleTime - 10) : 0
);
});
startDragging = (x: number, y: number, dropAction: dropActionType, hideSource = false) => this.docView?.startDragging(x, y, dropAction, hideSource);
+ @observable textHtmlOverlay: Opt<string>;
+ @computed get anchorViewDoc() {
+ return this.props.LayoutTemplateString?.includes('anchor2') ? DocCast(this.rootDoc['anchor2']) : this.props.LayoutTemplateString?.includes('anchor1') ? DocCast(this.rootDoc['anchor1']) : undefined;
+ }
docViewPathFunc = () => this.docViewPath;
isSelected = (outsideReaction?: boolean) => SelectionManager.IsSelected(this, outsideReaction);
select = (extendSelection: boolean) => SelectionManager.SelectView(this, !SelectionManager.Views().some(v => v.props.Document === this.props.ContainingCollectionDoc) && extendSelection);
@@ -1657,11 +1784,12 @@ export class DocumentView extends React.Component<DocumentViewProps> {
PanelHeight = () => this.panelHeight;
NativeDimScaling = () => this.nativeScaling;
selfView = () => this;
+ trueNativeWidth = () => returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, false));
screenToLocalTransform = () =>
this.props
.ScreenToLocalTransform()
.translate(-this.centeringX, -this.centeringY)
- .scale(1 / this.nativeScaling);
+ .scale(this.trueNativeWidth() ? 1 / this.nativeScaling : 1);
componentDidMount() {
this._disposers.reactionScript = reaction(
() => ScriptCast(this.rootDoc.reactionScript)?.script?.run({ this: this.props.Document, self: Cast(this.rootDoc, Doc, null) || this.props.Document }).result,
@@ -1680,14 +1808,48 @@ export class DocumentView extends React.Component<DocumentViewProps> {
Object.values(this._disposers).forEach(disposer => disposer?.());
!BoolCast(this.props.Document.dontRegisterView, this.props.dontRegisterView) && DocumentManager.Instance.RemoveView(this);
}
+ _hoverTimeout: any = undefined;
+ isHovering = () => this._isHovering;
+ @observable _isHovering = false;
+
+ htmlOverlayEffect = '';
+ @computed get htmlOverlay() {
+ const effectProps = {
+ delay: 0,
+ duration: 500,
+ };
+ const highlight = (
+ <div className="webBox-textHighlight">
+ <ObserverJsxParser autoCloseVoidElements={true} key={42} onError={(e: any) => console.log('PARSE error', e)} renderInWrapper={false} jsx={StrCast(this.textHtmlOverlay)} />
+ </div>
+ );
+ return !this.textHtmlOverlay ? null : (
+ <div className="documentView-htmlOverlay">
+ <div className="documentView-htmlOverlayInner">{<Fade {...effectProps}>{DocumentViewInternal.AnimationEffect(highlight, { presEffect: this.htmlOverlayEffect ?? 'Zoom' } as any as Doc, this.rootDoc)} </Fade>}</div>
+ </div>
+ );
+ }
render() {
TraceMobx();
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 isButton = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear;
+
return (
- <div className="contentFittingDocumentView">
+ <div
+ className="contentFittingDocumentView"
+ onPointerEnter={action(() => {
+ clearTimeout(this._hoverTimeout);
+ this._isHovering = true;
+ })}
+ onPointerLeave={action(() => {
+ clearTimeout(this._hoverTimeout);
+ this._hoverTimeout = setTimeout(
+ action(() => (this._isHovering = false)),
+ 500
+ );
+ })}>
{!this.props.Document || !this.props.PanelWidth() ? null : (
<div
className="contentFittingDocumentView-previewDoc"
@@ -1712,12 +1874,16 @@ export class DocumentView extends React.Component<DocumentViewProps> {
NativeDimScaling={this.NativeDimScaling}
isSelected={this.isSelected}
select={this.select}
+ isHovering={this.isHovering}
ScreenToLocalTransform={this.screenToLocalTransform}
focus={this.props.focus || emptyFunction}
ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))}
/>
+ {this.htmlOverlay}
</div>
)}
+
+ {this.linkCountView}
</div>
);
}
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 04d252abe..78ba499ec 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -33,7 +33,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
constructor(props: Readonly<FieldViewProps>) {
super(props);
const targetDoc = FilterBox.targetDoc;
- if (targetDoc && !targetDoc.currentFilter) targetDoc.currentFilter = FilterBox.createFilterDoc();
+ if (targetDoc && !targetDoc.currentFilter) runInAction(() => (targetDoc.currentFilter = FilterBox.createFilterDoc()));
}
/// creates a new, empty filter doc
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index e09155ac2..24562ccbd 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -13,6 +13,7 @@ import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { undoBatch } from '../../util/UndoManager';
import { ViewBoxBaseComponent } from '../DocComponent';
+import { DocFocusOptions } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
const EquationSchema = createSchema({});
@@ -47,7 +48,7 @@ export class FunctionPlotBox extends ViewBoxBaseComponent<FieldViewProps>() {
return anchor;
};
@action
- scrollFocus = (doc: Doc, smooth: boolean) => {
+ scrollFocus = (doc: Doc, smooth: DocFocusOptions) => {
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;
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index cd2b23f02..6359a9491 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -5,7 +5,6 @@
position: relative;
transform-origin: top left;
-
.imageBox-annotationLayer {
position: absolute;
transform-origin: left top;
@@ -95,7 +94,6 @@
height: 100%;
}
-
.imageBox-fader {
position: relative;
width: 100%;
@@ -103,7 +101,8 @@
display: flex;
height: 100%;
- .imageBox-fadeBlocker, .imageBox-fadeBlocker-hover{
+ .imageBox-fadeBlocker,
+ .imageBox-fadeBlocker-hover {
width: 100%;
height: 100%;
position: absolute;
@@ -126,17 +125,6 @@
left: 0;
}
-.imageBox-fadeBlocker {
- -webkit-transition: opacity 1s ease-in-out;
- -moz-transition: opacity 1s ease-in-out;
- -o-transition: opacity 1s ease-in-out;
- transition: opacity 1s ease-in-out;
-}
-
.imageBox-fadeBlocker-hover {
- -webkit-transition: opacity 1s ease-in-out;
- -moz-transition: opacity 1s ease-in-out;
- -o-transition: opacity 1s ease-in-out;
- transition: opacity 1s ease-in-out;
opacity: 0;
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 461d6984d..ac953d13b 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -8,13 +8,13 @@ import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
import { createSchema } from '../../../fields/Schema';
import { ComputedField } from '../../../fields/ScriptField';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
+import { emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
-import { DocUtils } from '../../documents/Documents';
+import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
import { Networking } from '../../Network';
import { DragManager } from '../../util/DragManager';
@@ -30,6 +30,8 @@ import { FaceRectangles } from './FaceRectangles';
import { FieldView, FieldViewProps } from './FieldView';
import './ImageBox.scss';
import React = require('react');
+import { PresBox } from './trails';
+import { DocFocusOptions, DocumentViewProps } from './DocumentView';
export const pageSchema = createSchema({
googlePhotosUrl: 'string',
@@ -54,24 +56,53 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@observable _curSuffix = '';
@observable _uploadIcon = uploadIcons.idle;
+ constructor(props: any) {
+ super(props);
+ this.props.setContentView?.(this);
+ }
+
protected createDropTarget = (ele: HTMLDivElement) => {
this._dropDisposer?.();
ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
};
- setViewSpec = (anchor: Doc, preview: boolean) => {}; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
+
+ @action
+ scrollFocus = (anchor: Doc, options: DocFocusOptions) => {
+ const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500;
+ return PresBox.restoreTargetDocView(
+ this.props.DocumentView?.(), //
+ { pinDocLayout: BoolCast(anchor.presPinDocLayout) },
+ anchor,
+ focusSpeed,
+ !anchor.presPinData
+ ? {}
+ : {
+ pannable: true,
+ dataannos: anchor.presAnnotations !== undefined,
+ dataview: true,
+ }
+ )
+ ? focusSpeed
+ : undefined;
+ }; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
getAnchor = () => {
- const anchor = this._getAnchor?.(this._savedAnnotations);
- anchor && this.addDocument(anchor);
- return anchor ?? this.rootDoc;
+ const anchor =
+ this._getAnchor?.(this._savedAnnotations) ?? // use marquee anchor, otherwise, save zoom/pan as anchor
+ Docs.Create.ImageanchorDocument({ presTransition: 1000, unrendered: true, annotationOn: this.rootDoc });
+ if (anchor) {
+ PresBox.pinDocView(anchor, { pinData: { pannable: true, dataview: true, dataannos: true } }, this.rootDoc);
+ this.addDocument(anchor);
+ return anchor;
+ }
+ return this.rootDoc;
};
componentDidMount() {
- this.props.setContentView?.(this); // bcz: do not remove this. without it, stepping into an image in the lightbox causes an infinite loop....
this._disposers.sizer = reaction(
() => ({
forceFull: this.props.renderDepth < 1 || this.layoutDoc._showFullRes,
- scrSize: this.props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth,
+ scrSize: (this.props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth) * NumCast(this.rootDoc._viewScale, 1),
selected: this.props.isSelected(),
}),
({ forceFull, scrSize, selected }) => (this._curSuffix = selected ? '_o' : this.fieldKey === 'icon' ? '_m' : forceFull ? '_o' : scrSize < 0.25 ? '_s' : scrSize < 0.5 ? '_m' : scrSize < 0.8 ? '_l' : '_o'),
@@ -120,6 +151,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@undoBatch
resolution = () => (this.layoutDoc._showFullRes = !this.layoutDoc._showFullRes);
+ @undoBatch
+ setUseAlt = () => (this.layoutDoc[this.fieldKey + '-useAlt'] = !this.layoutDoc[this.fieldKey + '-useAlt']);
@undoBatch
rotate = action(() => {
@@ -137,10 +170,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
crop = (region: Doc | undefined, addCrop?: boolean) => {
if (!region) return;
const cropping = Doc.MakeCopy(region, true);
- Doc.GetProto(region).backgroundColor = 'transparent';
Doc.GetProto(region).lockedPosition = true;
Doc.GetProto(region).title = 'region:' + this.rootDoc.title;
- Doc.GetProto(region).isPushpin = true;
+ Doc.GetProto(region).followLinkToggle = true;
this.addDocument(region);
const anchx = NumCast(cropping.x);
const anchy = NumCast(cropping.y);
@@ -183,6 +215,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const funcs: ContextMenuProps[] = [];
funcs.push({ description: 'Rotate Clockwise 90', event: this.rotate, icon: 'expand-arrows-alt' });
funcs.push({ description: `Show ${this.layoutDoc._showFullRes ? 'Dynamic Res' : 'Full Res'}`, event: this.resolution, icon: 'expand-arrows-alt' });
+ funcs.push({ description: `${this.layoutDoc[this.fieldKey + '-useAlt'] ? 'Show Alternate' : 'Show Primary'}`, event: this.setUseAlt, icon: 'expand-arrows-alt' });
funcs.push({ description: 'Copy path', event: () => Utils.CopyText(this.choosePath(field.url)), icon: 'expand-arrows-alt' });
if (!Doc.noviceMode) {
funcs.push({ description: 'Export to Google Photos', event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: 'caret-square-right' });
@@ -281,9 +314,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
setTimeout(
action(() => {
this._uploadIcon = idle;
- if (data) {
- dataDoc[this.fieldKey] = data;
- }
+ data && (dataDoc[this.fieldKey] = data);
}),
2000
);
@@ -315,7 +346,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
TraceMobx();
const srcpath = this.paths[0];
- const fadepath = this.paths[Math.min(1, this.paths.length - 1)];
+ const fadepath = this.paths.lastElement();
const { nativeWidth, nativeHeight, nativeOrientation } = this.nativeSize;
const rotation = NumCast(this.dataDoc[this.fieldKey + '-rotation']);
const aspect = rotation % 180 ? nativeHeight / nativeWidth : 1;
@@ -336,8 +367,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
<div className="imageBox-fader" style={{ overflow: Array.from(this.props.docViewPath?.()).slice(-1)[0].fitWidth ? 'auto' : undefined }}>
<img key="paths" src={srcpath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
{fadepath === srcpath ? null : (
- <div className={`imageBox-fadeBlocker${this.props.isHovering?.() ? '-hover' : ''}`}>
- <img className="imageBox-fadeaway" key={'fadeaway'} src={fadepath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
+ <div
+ className={`imageBox-fadeBlocker${(this.props.isHovering?.() && this.layoutDoc[this.fieldKey + '-useAlt'] === undefined) || BoolCast(this.layoutDoc[this.fieldKey + '-useAlt']) ? '-hover' : ''}`}
+ style={{ transition: StrCast(this.layoutDoc.viewTransition, 'opacity 1000ms') }}>
+ <img className="imageBox-fadeaway" key="fadeaway" src={fadepath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
</div>
)}
</div>
@@ -382,6 +415,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this.props.select(false);
};
savedAnnotations = () => this._savedAnnotations;
+ styleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
+ if (property === StyleProp.BoxShadow) return undefined;
+ return this.props.styleProvider?.(doc, props, property);
+ };
render() {
TraceMobx();
@@ -402,6 +439,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
{...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
renderDepth={this.props.renderDepth + 1}
fieldKey={this.annotationKey}
+ styleProvider={this.styleProvider}
CollectionView={undefined}
isAnnotationOverlay={true}
annotationLayerHostsContent={true}
@@ -427,8 +465,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
addDocument={this.addDocument}
finishMarquee={this.finishMarquee}
savedAnnotations={this.savedAnnotations}
+ selectionText={returnEmptyString}
annotationLayer={this._annotationLayer.current}
mainCont={this._mainCont.current}
+ highlightDragSrcColor={''}
anchorMenuCrop={this.crop}
/>
)}
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 7d04c4b64..18c5b81ec 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -20,6 +20,7 @@ import { ContextMenuProps } from '../ContextMenuItem';
import e = require('express');
import { FormattedTextBox } from './formattedText/FormattedTextBox';
import { ImageBox } from './ImageBox';
+import { OpenWhere } from './DocumentView';
export type KVPScript = {
script: CompiledScript;
@@ -259,8 +260,8 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
openItems.push({
description: 'Default Perspective',
event: () => {
- this.props.addDocTab(this.props.Document, 'close');
- this.props.addDocTab(this.fieldDocToLayout, 'add:right');
+ this.props.addDocTab(this.props.Document, OpenWhere.close);
+ this.props.addDocTab(this.fieldDocToLayout, OpenWhere.addRight);
},
icon: 'image',
});
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 80def3025..e74ef4a39 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,18 +1,19 @@
import { action, observable } from 'mobx';
-import { observer } from "mobx-react";
+import { observer } from 'mobx-react';
import { Doc, Field, Opt } from '../../../fields/Doc';
import { emptyFunction, returnFalse, returnOne, returnZero, returnEmptyFilter, returnEmptyDoclist, emptyPath } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { Transform } from '../../util/Transform';
import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
-import { EditableView } from "../EditableView";
+import { EditableView } from '../EditableView';
import { FieldView, FieldViewProps } from './FieldView';
import { KeyValueBox } from './KeyValueBox';
-import "./KeyValueBox.scss";
-import "./KeyValuePair.scss";
-import React = require("react");
+import './KeyValueBox.scss';
+import './KeyValuePair.scss';
+import React = require('react');
import { DefaultStyleProvider } from '../StyleProvider';
+import { OpenWhere } from './DocumentView';
// Represents one row in a key value plane
@@ -23,7 +24,7 @@ export interface KeyValuePairProps {
keyWidth: number;
PanelHeight: () => number;
PanelWidth: () => number;
- addDocTab: (doc: Doc, where: string) => boolean;
+ addDocTab: (doc: Doc, where: OpenWhere) => boolean;
}
@observer
export class KeyValuePair extends React.Component<KeyValuePairProps> {
@@ -34,23 +35,23 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
@action
handleCheck = (e: React.ChangeEvent<HTMLInputElement>) => {
this.isChecked = e.currentTarget.checked;
- }
+ };
@action
uncheck = () => {
this.checkbox.current!.checked = false;
this.isChecked = false;
- }
+ };
onContextMenu = (e: React.MouseEvent) => {
const value = this.props.doc[this.props.keyName];
if (value instanceof Doc) {
e.stopPropagation();
e.preventDefault();
- ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
+ ContextMenu.Instance.addItem({ description: 'Open Fields', event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { _width: 300, _height: 300 }), OpenWhere.addRight), icon: 'layer-group' });
ContextMenu.Instance.displayMenu(e.clientX, e.clientY);
}
- }
+ };
render() {
const props: FieldViewProps = {
@@ -68,7 +69,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
isSelected: returnFalse,
setHeight: returnFalse,
select: emptyFunction,
- dropAction: "alias",
+ dropAction: 'alias',
bringToFront: emptyFunction,
renderDepth: 1,
isContentActive: returnFalse,
@@ -92,30 +93,30 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
doc = doc.proto;
}
const parenCount = Math.max(0, protoCount - 1);
- const keyStyle = protoCount === 0 ? "black" : "blue";
+ const keyStyle = protoCount === 0 ? 'black' : 'blue';
- const hover = { transition: "0.3s ease opacity", opacity: this.isPointerOver || this.isChecked ? 1 : 0 };
+ const hover = { transition: '0.3s ease opacity', opacity: this.isPointerOver || this.isChecked ? 1 : 0 };
return (
- <tr className={this.props.rowStyle} onPointerEnter={action(() => this.isPointerOver = true)} onPointerLeave={action(() => this.isPointerOver = false)}>
+ <tr className={this.props.rowStyle} onPointerEnter={action(() => (this.isPointerOver = true))} onPointerLeave={action(() => (this.isPointerOver = false))}>
<td className="keyValuePair-td-key" style={{ width: `${this.props.keyWidth}%` }}>
<div className="keyValuePair-td-key-container">
- <button style={hover} className="keyValuePair-td-key-delete" onClick={undoBatch(() => {
- if (Object.keys(props.Document).indexOf(props.fieldKey) !== -1) {
- delete props.Document[props.fieldKey];
- }
- else delete props.Document.proto![props.fieldKey];
- })}>
+ <button
+ style={hover}
+ className="keyValuePair-td-key-delete"
+ onClick={undoBatch(() => {
+ if (Object.keys(props.Document).indexOf(props.fieldKey) !== -1) {
+ delete props.Document[props.fieldKey];
+ } else delete props.Document.proto![props.fieldKey];
+ })}>
X
</button>
- <input
- className={"keyValuePair-td-key-check"}
- type="checkbox"
- style={hover}
- onChange={this.handleCheck}
- ref={this.checkbox}
- />
- <div className="keyValuePair-keyField" style={{ color: keyStyle }}>{"(".repeat(parenCount)}{props.fieldKey}{")".repeat(parenCount)}</div>
+ <input className={'keyValuePair-td-key-check'} type="checkbox" style={hover} onChange={this.handleCheck} ref={this.checkbox} />
+ <div className="keyValuePair-keyField" style={{ color: keyStyle }}>
+ {'('.repeat(parenCount)}
+ {props.fieldKey}
+ {')'.repeat(parenCount)}
+ </div>
</div>
</td>
<td className="keyValuePair-td-value" style={{ width: `${100 - this.props.keyWidth}%` }} onContextMenu={this.onContextMenu}>
@@ -123,13 +124,13 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
<EditableView
contents={contents}
maxHeight={36}
- height={"auto"}
+ height={'auto'}
GetValue={() => Field.toKeyValueString(props.Document, props.fieldKey)}
- SetValue={(value: string) =>
- KeyValueBox.SetField(props.Document, props.fieldKey, value)} />
+ SetValue={(value: string) => KeyValueBox.SetField(props.Document, props.fieldKey, value)}
+ />
</div>
</td>
</tr>
);
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index b58a9affb..10897b48f 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -15,16 +15,17 @@ import { FieldView, FieldViewProps } from './FieldView';
import BigText from './LabelBigText';
import './LabelBox.scss';
-
export interface LabelBoxProps {
label?: string;
}
@observer
-export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxProps)>() {
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LabelBox, fieldKey); }
+export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProps>() {
+ public static LayoutString(fieldKey: string) {
+ return FieldView.LayoutString(LabelBox, fieldKey);
+ }
public static LayoutStringWithTitle(fieldStr: string, label?: string) {
- return !label ? LabelBox.LayoutString(fieldStr) : `<LabelBox fieldKey={'${fieldStr}'} label={'${label}'} {...props} />`; //e.g., "<ImageBox {...props} fieldKey={"data} />"
+ return !label ? LabelBox.LayoutString(fieldStr) : `<LabelBox fieldKey={'${fieldStr}'} label={'${label}'} {...props} />`; //e.g., "<ImageBox {...props} fieldKey={"data} />"
}
private dropDisposer?: DragManager.DragDropDisposer;
private _timeout: any;
@@ -35,11 +36,12 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
this._timeout && clearTimeout(this._timeout);
}
+ getAnchor = () => {
+ return this.rootDoc;
+ };
+
getTitle() {
- return this.rootDoc["title-custom"] ? StrCast(this.rootDoc.title) :
- this.props.label ? this.props.label :
- typeof this.rootDoc[this.fieldKey] === "string" ? StrCast(this.rootDoc[this.fieldKey]) :
- StrCast(this.rootDoc.title);
+ return this.rootDoc['title-custom'] ? StrCast(this.rootDoc.title) : this.props.label ? this.props.label : typeof this.rootDoc[this.fieldKey] === 'string' ? StrCast(this.rootDoc[this.fieldKey]) : StrCast(this.rootDoc.title);
}
protected createDropTarget = (ele: HTMLDivElement) => {
@@ -47,36 +49,42 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
if (ele) {
this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document);
}
- }
+ };
- get paramsDoc() { return Doc.AreProtosEqual(this.layoutDoc, this.dataDoc) ? this.dataDoc : this.layoutDoc; }
+ get paramsDoc() {
+ return Doc.AreProtosEqual(this.layoutDoc, this.dataDoc) ? this.dataDoc : this.layoutDoc;
+ }
specificContextMenu = (e: React.MouseEvent): void => {
const funcs: ContextMenuProps[] = [];
- !Doc.noviceMode && funcs.push({
- description: "Clear Script Params", event: () => {
- const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []);
- params?.map(p => this.paramsDoc[p] = undefined);
- }, icon: "trash"
- });
+ !Doc.noviceMode &&
+ funcs.push({
+ description: 'Clear Script Params',
+ event: () => {
+ const params = Cast(this.paramsDoc['onClick-paramFieldKeys'], listSpec('string'), []);
+ params?.map(p => (this.paramsDoc[p] = undefined));
+ },
+ icon: 'trash',
+ });
- funcs.length && ContextMenu.Instance.addItem({ description: "OnClick...", noexpand: true, subitems: funcs, icon: "mouse-pointer" });
- }
+ funcs.length && ContextMenu.Instance.addItem({ description: 'OnClick...', noexpand: true, subitems: funcs, icon: 'mouse-pointer' });
+ };
@undoBatch
@action
drop = (e: Event, de: DragManager.DropEvent) => {
const docDragData = de.complete.docDragData;
- const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []);
+ const params = Cast(this.paramsDoc['onClick-paramFieldKeys'], listSpec('string'), []);
const missingParams = params?.filter(p => !this.paramsDoc[p]);
if (docDragData && missingParams?.includes((e.target as any).textContent)) {
- this.paramsDoc[(e.target as any).textContent] = new List<Doc>(docDragData.droppedDocuments.map((d, i) =>
- d.onDragStart ? docDragData.draggedDocuments[i] : d));
+ this.paramsDoc[(e.target as any).textContent] = new List<Doc>(docDragData.droppedDocuments.map((d, i) => (d.onDragStart ? docDragData.draggedDocuments[i] : d)));
e.stopPropagation();
}
- }
+ };
@observable _mouseOver = false;
- @computed get hoverColor() { return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : "unset"; }
+ @computed get hoverColor() {
+ return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : 'unset';
+ }
fitTextToBox = (r: any): any => {
const singleLine = BoolCast(this.rootDoc._singleLine, true);
@@ -85,63 +93,73 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
fontSizeFactor: 1,
minimumFontSize: NumCast(this.rootDoc._minFontSize, 8),
maximumFontSize: NumCast(this.rootDoc._maxFontSize, 1000),
- limitingDimension: "both",
- horizontalAlign: "center",
- verticalAlign: "center",
- textAlign: "center",
+ limitingDimension: 'both',
+ horizontalAlign: 'center',
+ verticalAlign: 'center',
+ textAlign: 'center',
singleLine,
- whiteSpace: singleLine ? "nowrap" : "pre-wrap"
+ whiteSpace: singleLine ? 'nowrap' : 'pre-wrap',
};
this._timeout = undefined;
if (!r) return params;
- if (!r.offsetHeight || !r.offsetWidth) return this._timeout = setTimeout(() => this.fitTextToBox(r));
+ if (!r.offsetHeight || !r.offsetWidth) return (this._timeout = setTimeout(() => this.fitTextToBox(r)));
const parent = r.parentNode;
const parentStyle = parent.style;
- parentStyle.display = "";
- parentStyle.alignItems = "";
- r.setAttribute("style", "");
- r.style.width = singleLine ? "" : "100%";
+ parentStyle.display = '';
+ parentStyle.alignItems = '';
+ r.setAttribute('style', '');
+ r.style.width = singleLine ? '' : '100%';
- r.style.textOverflow = "ellipsis";
- r.style.overflow = "hidden";
+ r.style.textOverflow = 'ellipsis';
+ r.style.overflow = 'hidden';
BigText(r, params);
return params;
- }
+ };
// (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")")
render() {
- const boxParams = this.fitTextToBox(null);// this causes mobx to trigger re-render when data changes
- const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []);
+ const boxParams = this.fitTextToBox(null); // this causes mobx to trigger re-render when data changes
+ const params = Cast(this.paramsDoc['onClick-paramFieldKeys'], listSpec('string'), []);
const missingParams = params?.filter(p => !this.paramsDoc[p]);
params?.map(p => DocListCast(this.paramsDoc[p])); // bcz: really hacky form of prefetching ...
const label = this.getTitle();
return (
- <div className="labelBox-outerDiv"
- onMouseLeave={action(() => this._mouseOver = false)}
- onMouseOver={action(() => this._mouseOver = true)}
- ref={this.createDropTarget} onContextMenu={this.specificContextMenu}
+ <div
+ className="labelBox-outerDiv"
+ onMouseLeave={action(() => (this._mouseOver = false))}
+ onMouseOver={action(() => (this._mouseOver = true))}
+ ref={this.createDropTarget}
+ onContextMenu={this.specificContextMenu}
style={{ boxShadow: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow) }}>
- <div className="labelBox-mainButton" style={{
- backgroundColor: this.hoverColor,
- fontSize: StrCast(this.layoutDoc._fontSize),
- fontFamily: StrCast(this.layoutDoc._fontFamily) || "inherit",
- letterSpacing: StrCast(this.layoutDoc.letterSpacing),
- textTransform: StrCast(this.layoutDoc.textTransform) as any,
- paddingLeft: NumCast(this.rootDoc._xPadding),
- paddingRight: NumCast(this.rootDoc._xPadding),
- paddingTop: NumCast(this.rootDoc._yPadding),
- paddingBottom: NumCast(this.rootDoc._yPadding),
- width: this.props.PanelWidth(),
- height: this.props.PanelHeight(),
- whiteSpace: boxParams.singleLine ? "pre" : "pre-wrap"
- }} >
- <span style={{ width: boxParams.singleLine ? "" : "100%" }} ref={action((r: any) => this.fitTextToBox(r))}>
- {label.startsWith("#") ? (null) : label.replace(/([^a-zA-Z])/g, "$1\u200b")}
+ <div
+ className="labelBox-mainButton"
+ style={{
+ backgroundColor: this.hoverColor,
+ fontSize: StrCast(this.layoutDoc._fontSize),
+ fontFamily: StrCast(this.layoutDoc._fontFamily) || 'inherit',
+ letterSpacing: StrCast(this.layoutDoc.letterSpacing),
+ textTransform: StrCast(this.layoutDoc.textTransform) as any,
+ paddingLeft: NumCast(this.rootDoc._xPadding),
+ paddingRight: NumCast(this.rootDoc._xPadding),
+ paddingTop: NumCast(this.rootDoc._yPadding),
+ paddingBottom: NumCast(this.rootDoc._yPadding),
+ width: this.props.PanelWidth(),
+ height: this.props.PanelHeight(),
+ whiteSpace: boxParams.singleLine ? 'pre' : 'pre-wrap',
+ }}>
+ <span style={{ width: boxParams.singleLine ? '' : '100%' }} ref={action((r: any) => this.fitTextToBox(r))}>
+ {label.startsWith('#') ? null : label.replace(/([^a-zA-Z])/g, '$1\u200b')}
</span>
</div>
- <div className="labelBox-fieldKeyParams" >
- {!missingParams?.length ? (null) : missingParams.map(m => <div key={m} className="labelBox-missingParam">{m}</div>)}
+ <div className="labelBox-fieldKeyParams">
+ {!missingParams?.length
+ ? null
+ : missingParams.map(m => (
+ <div key={m} className="labelBox-missingParam">
+ {m}
+ </div>
+ ))}
</div>
</div>
);
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 5102eae51..e89076c1f 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -1,4 +1,3 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import { Doc } from '../../../fields/Doc';
@@ -7,19 +6,17 @@ import { TraceMobx } from '../../../fields/util';
import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils';
import { DragManager } from '../../util/DragManager';
import { LinkFollower } from '../../util/LinkFollower';
-import { SelectionManager } from '../../util/SelectionManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxBaseComponent } from '../DocComponent';
-import { LinkEditor } from '../linking/LinkEditor';
import { StyleProp } from '../StyleProvider';
import { FieldView, FieldViewProps } from './FieldView';
import './LinkAnchorBox.scss';
import { LinkDocPreview } from './LinkDocPreview';
import React = require('react');
-const higflyout = require('@hig/flyout');
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
+import { LinkManager } from '../../util/LinkManager';
+import globalCssVariables = require('../global/globalCssVariables.scss');
+import { SelectionManager } from '../../util/SelectionManager';
@observer
export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -33,15 +30,23 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
_timeout: NodeJS.Timeout | undefined;
@observable _x = 0;
@observable _y = 0;
- @observable _selected = false;
- @observable _editing = false;
- @observable _forceOpen = false;
onPointerDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, false);
+ const anchorContainerDoc = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.LinkSource);
+ setupMoveUpEvents(
+ this,
+ e,
+ this.onPointerMove,
+ emptyFunction,
+ (e, doubleTap) => {
+ if (doubleTap) LinkFollower.FollowLink(this.rootDoc, anchorContainerDoc, this.props, false);
+ else this.props.select(false);
+ },
+ false
+ );
};
onPointerMove = action((e: PointerEvent, down: number[], delta: number[]) => {
- const cdiv = this._ref && this._ref.current && this._ref.current.parentElement;
+ const cdiv = this._ref?.current?.parentElement;
if (!this._isOpen && cdiv) {
const bounds = cdiv.getBoundingClientRect();
const pt = Utils.getNearestPointInPerimeter(bounds.left, bounds.top, bounds.width, bounds.height, e.clientX, e.clientY);
@@ -59,58 +64,8 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
return false;
});
- @action
- onClick = (e: React.MouseEvent) => {
- if (e.button === 2 || e.ctrlKey || !this.layoutDoc.isLinkButton) {
- this.props.select(false);
- }
- if (!this._doubleTap && !e.ctrlKey && e.button < 2) {
- const anchorContainerDoc = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.LinkSource);
- this._editing = true;
- anchorContainerDoc && this.props.bringToFront(anchorContainerDoc, false);
- if (anchorContainerDoc && !this.layoutDoc.onClick && !this._isOpen) {
- this._timeout = setTimeout(
- action(() => {
- LinkFollower.FollowLink(this.rootDoc, anchorContainerDoc, this.props, false);
- this._editing = false;
- }),
- 300 - (Date.now() - this._lastTap)
- );
- e.stopPropagation();
- }
- } else {
- this._timeout && clearTimeout(this._timeout);
- this._timeout = undefined;
- this._doubleTap = false;
- this.openLinkEditor(e);
- e.stopPropagation();
- }
- };
- openLinkDocOnRight = (e: React.MouseEvent) => {
- this.props.addDocTab(this.rootDoc, 'add:right');
- };
- openLinkTargetOnRight = (e: React.MouseEvent) => {
- const alias = Doc.MakeAlias(Cast(this.layoutDoc[this.fieldKey], Doc, null));
- alias._isLinkButton = undefined;
- alias.layoutKey = 'layout';
- this.props.addDocTab(alias, 'add:right');
- };
- @action
- openLinkEditor = action((e: React.MouseEvent) => {
- SelectionManager.DeselectAll();
- this._editing = this._forceOpen = true;
- });
-
- specificContextMenu = (e: React.MouseEvent): void => {
- const funcs: ContextMenuProps[] = [];
- funcs.push({ description: 'Open Link Target on Right', event: () => this.openLinkTargetOnRight(e), icon: 'eye' });
- funcs.push({ description: 'Open Link on Right', event: () => this.openLinkDocOnRight(e), icon: 'eye' });
- funcs.push({ description: 'Open Link Editor', event: () => this.openLinkEditor(e), icon: 'eye' });
- funcs.push({ description: 'Toggle Always Show Link', event: () => (this.props.Document.linkDisplay = !this.props.Document.linkDisplay), icon: 'eye' });
-
- ContextMenu.Instance.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' });
- };
+ specificContextMenu = (e: React.MouseEvent): void => {};
render() {
TraceMobx();
@@ -121,22 +76,13 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
const background = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.BackgroundColor + ':anchor');
const anchor = this.fieldKey === 'anchor1' ? 'anchor2' : 'anchor1';
const anchorScale = !this.dataDoc[this.fieldKey + '-useLinkSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25;
-
const targetTitle = StrCast((this.dataDoc[anchor] as Doc)?.title);
- const flyout = (
- <div className="linkAnchorBoxBox-flyout" title=" " onPointerOver={() => Doc.UnBrushDoc(this.rootDoc)}>
- <LinkEditor sourceDoc={Cast(this.dataDoc[this.fieldKey], Doc, null)} hideback={true} linkDoc={this.rootDoc} showLinks={action(() => {})} />
- {!this._forceOpen ? null : (
- <div className="linkAnchorBox-linkCloser" onPointerDown={action(() => (this._isOpen = this._editing = this._forceOpen = false))}>
- <FontAwesomeIcon color="dimgray" icon={'times'} size={'sm'} />
- </div>
- )}
- </div>
- );
+ const selView = SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('anchor1') ? 'anchor1' : SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('anchor2') ? 'anchor2' : '';
return (
<div
+ ref={this._ref}
+ title={targetTitle}
className={`linkAnchorBox-cont${small ? '-small' : ''}`}
- //onPointerLeave={} //LinkDocPreview.Clear}
onPointerEnter={e =>
LinkDocPreview.SetLinkInfo({
docProps: this.props,
@@ -144,27 +90,19 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
linkDoc: this.rootDoc,
showHeader: true,
location: [e.clientX, e.clientY + 20],
+ noPreview: false,
})
}
onPointerDown={this.onPointerDown}
- onClick={this.onClick}
- title={targetTitle}
onContextMenu={this.specificContextMenu}
- ref={this._ref}
style={{
+ border: selView && this.rootDoc[selView] === this.rootDoc[this.fieldKey] ? `solid ${globalCssVariables.MEDIUM_GRAY} 2px` : undefined,
background,
left: `calc(${x}% - ${small ? 2.5 : 7.5}px)`,
top: `calc(${y}% - ${small ? 2.5 : 7.5}px)`,
transform: `scale(${anchorScale})`,
- }}>
- {!this._editing && !this._forceOpen ? null : (
- <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout} open={this._forceOpen ? true : undefined} onOpen={() => (this._isOpen = true)} onClose={action(() => (this._isOpen = this._forceOpen = this._editing = false))}>
- <span className="linkAnchorBox-button">
- <FontAwesomeIcon icon={'eye'} size={'lg'} />
- </span>
- </Flyout>
- )}
- </div>
+ }}
+ />
);
}
}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 27e79a83b..232c3459e 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -12,12 +12,11 @@ import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
import { LinkFollower } from '../../util/LinkFollower';
import { LinkManager } from '../../util/LinkManager';
+import { SettingsManager } from '../../util/SettingsManager';
import { Transform } from '../../util/Transform';
-import { undoBatch } from '../../util/UndoManager';
-import { DocumentView, DocumentViewSharedProps } from './DocumentView';
+import { DocumentView, DocumentViewSharedProps, OpenWhere } from './DocumentView';
import './LinkDocPreview.scss';
import React = require('react');
-import { LinkEditor } from '../linking/LinkEditor';
interface LinkDocPreviewProps {
linkDoc?: Doc;
@@ -26,6 +25,7 @@ interface LinkDocPreviewProps {
location: number[];
hrefs?: string[];
showHeader?: boolean;
+ noPreview?: boolean;
}
@observer
export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
@@ -40,6 +40,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
_linkDocRef = React.createRef<HTMLDivElement>();
@observable public static LinkInfo: Opt<LinkDocPreviewProps>;
@observable _targetDoc: Opt<Doc>;
+ @observable _markerTargetDoc: Opt<Doc>;
@observable _linkDoc: Opt<Doc>;
@observable _linkSrc: Opt<Doc>;
@observable _toolTipText = '';
@@ -56,9 +57,9 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
}
if (linkTarget?.annotationOn && linkTarget?.type !== DocumentType.RTF) {
// want to show annotation context document if annotation is not text
- linkTarget && DocCastAsync(linkTarget.annotationOn).then(action(anno => (this._targetDoc = anno)));
+ linkTarget && DocCastAsync(linkTarget.annotationOn).then(action(anno => (this._markerTargetDoc = this._targetDoc = anno)));
} else {
- this._targetDoc = linkTarget;
+ this._markerTargetDoc = this._targetDoc = linkTarget;
}
this._toolTipText = '';
}
@@ -104,13 +105,15 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
// automatic links specify the target in the link info, not the source
const linkTarget = anchor;
this._linkSrc = LinkManager.getOppositeAnchor(this._linkDoc, linkTarget);
- this._targetDoc = linkTarget;
+ this._markerTargetDoc = this._targetDoc = linkTarget;
} else {
this._linkSrc = anchor;
const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
+ this._markerTargetDoc = linkTarget;
this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget;
}
this._toolTipText = '';
+ if (LinkDocPreview.LinkInfo?.noPreview || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink();
}
})
);
@@ -119,15 +122,22 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
}
return undefined;
}
- @observable _showEditor = false;
+
+ @action
editLink = (e: React.PointerEvent): void => {
- LinkManager.currentLink = this.props.linkDoc;
setupMoveUpEvents(
this,
e,
returnFalse,
emptyFunction,
- action(() => (this._showEditor = !this._showEditor))
+ action(() => {
+ LinkManager.currentLink = this._linkDoc;
+ LinkManager.currentLinkAnchor = this._linkSrc;
+ this.props.docProps.DocumentView?.().select(false);
+ if ((SettingsManager.propertiesWidth ?? 0) < 100) {
+ SettingsManager.propertiesWidth = 250;
+ }
+ })
);
};
nextHref = (e: React.PointerEvent) => {
@@ -152,7 +162,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
LinkDocPreview.Clear();
LinkFollower.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false);
} else if (this.props.hrefs?.length) {
- 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');
+ this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }), OpenWhere.addRight);
}
};
@@ -173,24 +183,25 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
return Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225));
};
@computed get previewHeader() {
- return !this._linkDoc || !this._targetDoc || !this._linkSrc ? null : (
+ return !this._linkDoc || !this._markerTargetDoc || !this._targetDoc || !this._linkSrc ? null : (
<div className="linkDocPreview-info">
+ <div className="linkDocPreview-buttonBar" style={{ float: 'left' }}>
+ <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>
<div className="linkDocPreview-title" style={{ pointerEvents: 'all' }}>
- {StrCast(this._targetDoc.title).length > 16 ? StrCast(this._targetDoc.title).substr(0, 16) + '...' : StrCast(this._targetDoc.title)}
+ {StrCast(this._markerTargetDoc.title).length > 16 ? StrCast(this._markerTargetDoc.title).substr(0, 16) + '...' : StrCast(this._markerTargetDoc.title)}
<p className="linkDocPreview-description"> {StrCast(this._linkDoc.description)}</p>
</div>
- <div className="linkDocPreview-buttonBar">
+ <div className="linkDocPreview-buttonBar" style={{ float: 'right' }}>
<Tooltip title={<div className="dash-tooltip">Next Link</div>} placement="top">
<div className="linkDocPreview-button" style={{ background: (this.props.hrefs?.length || 0) <= 1 ? 'gray' : 'green' }} onPointerDown={this.nextHref}>
<FontAwesomeIcon className="linkDocPreview-fa-icon" icon="chevron-right" color="white" size="sm" />
</div>
</Tooltip>
-
- <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>
</div>
);
@@ -274,9 +285,8 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
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))} />}
+ style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
+ {this.docPreview}
</div>
);
}
diff --git a/src/client/views/nodes/LoadingBox.scss b/src/client/views/nodes/LoadingBox.scss
index d63ed2575..4c3b8dabe 100644
--- a/src/client/views/nodes/LoadingBox.scss
+++ b/src/client/views/nodes/LoadingBox.scss
@@ -6,6 +6,13 @@
background-color: #fdfdfd;
height: 100%;
align-items: center;
+ .textContainer,
+ .text {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 80%;
+ text-align: center;
+ }
}
.textContainer {
@@ -17,14 +24,6 @@
align-content: center;
}
-.textContainer,
-.text {
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: 80%;
- text-align: center;
-}
-
.headerText {
text-align: center;
font-weight: bold;
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 6479e933e..c8d5b0154 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -7,7 +7,7 @@ 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 { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
import { Docs } from '../../../documents/Documents';
import { DragManager } from '../../../util/DragManager';
import { SnappingManager } from '../../../util/SnappingManager';
@@ -322,7 +322,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
*/
sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
console.log('print all sidebar Docs');
- console.log(this.allSidebarDocs);
if (!this.layoutDoc._showSidebar) this.toggleSidebar();
const docs = doc instanceof Doc ? [doc] : doc;
docs.forEach(doc => {
@@ -337,8 +336,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
}
}); //add to annotation list
- console.log('sidebaraddDocument');
- console.log(doc);
return this.addDocument(doc, sidebarKey); // add to sidebar list
};
@@ -352,10 +349,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
if (this.layoutDoc._showSidebar) this.toggleSidebar();
const docs = doc instanceof Doc ? [doc] : doc;
- docs.forEach(doc => {
- console.log(this.allMapMarkers);
- console.log(this.allSidebarDocs);
- });
return this.removeDocument(doc, sidebarKey);
};
@@ -405,7 +398,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
*/
@action
private handlePlaceChanged = () => {
- console.log(this.searchBox);
const place = this.searchBox.getPlace();
if (!place.geometry || !place.geometry.location) {
@@ -416,7 +408,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
// zoom in on the location of the search result
if (place.geometry.viewport) {
- console.log(this._map);
this._map.fitBounds(place.geometry.viewport);
} else {
this._map.setCenter(place.geometry.location);
@@ -638,6 +629,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
finishMarquee={this.finishMarquee}
savedAnnotations={this.savedAnnotations}
annotationLayer={this._annotationLayer.current}
+ selectionText={returnEmptyString}
mainCont={this._mainCont.current}
/>
)}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index c7001f846..e22ee5021 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -5,7 +5,7 @@ import * as Pdfjs from 'pdfjs-dist';
import 'pdfjs-dist/web/pdf_viewer.css';
import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
-import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField, PdfField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils';
@@ -27,6 +27,8 @@ import { ImageBox } from './ImageBox';
import './PDFBox.scss';
import { VideoBox } from './VideoBox';
import React = require('react');
+import { PresBox } from './trails';
+import { DocFocusOptions } from './DocumentView';
@observer
export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
@@ -74,7 +76,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (oldDiv.className === 'pdfBox-ui' || oldDiv.className === 'pdfViewerDash-overlay-inking') {
newDiv.style.display = 'none';
}
- if (newDiv && newDiv.style) newDiv.style.overflow = 'hidden';
+ if (newDiv?.style) newDiv.style.overflow = 'hidden';
if (oldDiv instanceof HTMLCanvasElement) {
const canvas = oldDiv;
const img = document.createElement('img'); // create a Image Element
@@ -93,7 +95,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const cropping = Doc.MakeCopy(region, true);
Doc.GetProto(region).lockedPosition = true;
Doc.GetProto(region).title = 'region:' + this.rootDoc.title;
- Doc.GetProto(region).isPushpin = true;
+ Doc.GetProto(region).followLinkToggle = true;
this.addDocument(region);
const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
@@ -199,24 +201,34 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
brushView = (view: { width: number; height: number; panX: number; panY: number }) => {
this._pdfViewer?.brushView(view);
};
- scrollFocus = (doc: Doc, smooth: boolean) => {
+ scrollFocus = (doc: Doc, options: DocFocusOptions) => {
let didToggle = false;
if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
- this.toggleSidebar(!smooth);
+ this.toggleSidebar(!options.instant);
didToggle = true;
}
if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
this._initialScrollTarget = doc;
- return this._pdfViewer?.scrollFocus(doc, smooth) ?? (didToggle ? 1 : undefined);
+ PresBox.restoreTargetDocView(this.props.DocumentView?.(), {}, doc, options.zoomTime ?? 500, { pannable: doc.presPinData ? true : false });
+ return this._pdfViewer?.scrollFocus(doc, NumCast(doc.presPinViewScroll, NumCast(doc.y)), options) ?? (didToggle ? 1 : undefined);
};
getAnchor = () => {
- const anchor =
- this._pdfViewer?._getAnchor(this._pdfViewer.savedAnnotations()) ??
- Docs.Create.TextanchorDocument({
+ let ele: Opt<HTMLDivElement> = undefined;
+ if (this._pdfViewer?.selectionContent()) {
+ ele = document.createElement('div');
+ ele.append(this._pdfViewer.selectionContent()!);
+ }
+ const docAnchor = () => {
+ const anchor = Docs.Create.TextanchorDocument({
title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)),
- y: NumCast(this.layoutDoc._scrollTop),
unrendered: true,
});
+ PresBox.pinDocView(anchor, { pinData: { scrollable: true, pannable: true } }, this.rootDoc);
+ return anchor;
+ };
+ const anchor = this._pdfViewer?._getAnchor(this._pdfViewer.savedAnnotations()) ?? docAnchor();
+ anchor.text = ele?.textContent ?? '';
+ anchor.textHtml = ele?.innerHTML;
this.addDocument(anchor);
return anchor;
};
@@ -273,7 +285,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
setPdfViewer = (pdfViewer: PDFViewer) => {
this._pdfViewer = pdfViewer;
if (this._initialScrollTarget) {
- this.scrollFocus(this._initialScrollTarget, false);
+ this.scrollFocus(this._initialScrollTarget, { instant: true });
this._initialScrollTarget = undefined;
}
};
@@ -398,9 +410,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
</div>
);
}
- sidebarWidth = () =>
- !this.SidebarShown ? 0 : PDFBox.sidebarResizerWidth + (this._previewWidth ? PDFBox.openSidebarWidth : ((NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth()) / NumCast(this.layoutDoc.nativeWidth));
-
+ sidebarWidth = () => {
+ if (!this.SidebarShown) return 0;
+ if (this._previewWidth) return PDFBox.sidebarResizerWidth + PDFBox.openSidebarWidth; // return default sidebar if previewing (as in viewing a link target)
+ const nativeDiff = NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc);
+ return PDFBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1);
+ };
specificContextMenu = (e: React.MouseEvent): void => {
const funcs: ContextMenuProps[] = [];
funcs.push({ description: 'Copy path', event: () => this.pdfUrl && Utils.CopyText(Utils.prepend('') + this.pdfUrl.url.pathname), icon: 'expand-arrows-alt' });
@@ -483,7 +498,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
crop={this.crop}
/>
</div>
- <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.layoutDoc[WidthSym]()}%` }}>
+ <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.props.PanelWidth()}%` }}>
<SidebarAnnos
ref={this._sidebarRef}
{...this.props}
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 4c8a836f1..281967a21 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -197,7 +197,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
const bindings: { [name: string]: any } = {};
this.paramsNames.forEach(key => (bindings[key] = this.rootDoc[key]));
// binds vars so user doesnt have to refer to everything as self.<var>
- ScriptCast(this.rootDoc[this.fieldKey], null)?.script.run({ self: this.rootDoc, this: this.layoutDoc, ...bindings }, this.onError);
+ ScriptCast(this.rootDoc[this.fieldKey], null)?.script.run({ ...bindings, self: this.rootDoc, this: this.layoutDoc }, this.onError);
}
};
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index fb47bfc07..a8f78edd5 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -9,7 +9,7 @@ import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { AudioField, ImageField, VideoField } from '../../../fields/URLField';
-import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
+import { emptyFunction, formatTime, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
import { Networking } from '../../Network';
@@ -31,6 +31,7 @@ import { FieldView, FieldViewProps } from './FieldView';
import { RecordingBox } from './RecordingBox';
import './VideoBox.scss';
import { ObjectField } from '../../../fields/ObjectField';
+import { DocFocusOptions, OpenWhere } from './DocumentView';
const path = require('path');
/**
@@ -273,7 +274,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this.player && this._contentRef && this._contentRef.requestFullscreen();
}
try {
- this._youtubePlayer && this.props.addDocTab(this.rootDoc, 'add');
+ this._youtubePlayer && this.props.addDocTab(this.rootDoc, OpenWhere.add);
} catch (e) {
console.log('Video FullScreen Exception:', e);
}
@@ -949,7 +950,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
);
};
- scrollFocus = (doc: Doc, smooth: boolean) => {
+ scrollFocus = (doc: Doc, options: DocFocusOptions) => {
if (doc !== this.rootDoc) {
const showTime = Cast(doc._timecodeToShow, 'number', null);
showTime !== undefined && setTimeout(() => this.Seek(showTime), 100);
@@ -960,7 +961,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// renders CollectionStackedTimeline
@computed get renderTimeline() {
return (
- <div className="videoBox-stackPanel" style={{ transition: this.transition, height: `${100 - this.heightPercent}%` }}>
+ <div className="videoBox-stackPanel" style={{ transition: this.transition, height: `${100 - this.heightPercent}%`, display: this.heightPercent === 100 ? 'none' : '' }}>
<CollectionStackedTimeline
ref={action((r: any) => (this._stackedTimeline = r))}
{...this.props}
@@ -1005,7 +1006,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
Doc.GetProto(region).backgroundColor = 'transparent';
Doc.GetProto(region).lockedPosition = true;
Doc.GetProto(region).title = 'region:' + this.rootDoc.title;
- Doc.GetProto(region).isPushpin = true;
+ Doc.GetProto(region).followLinkToggle = true;
region._timecodeToHide = NumCast(region._timecodeToShow) + 0.0001;
this.addDocument(region);
const anchx = NumCast(cropping.x);
@@ -1107,6 +1108,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
addDocument={this.addDocWithTimecode}
finishMarquee={this.finishMarquee}
savedAnnotations={this.savedAnnotations}
+ selectionText={returnEmptyString}
annotationLayer={this._annotationLayer.current}
mainCont={this._mainCont.current}
anchorMenuCrop={this.crop}
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index a41f66ef0..6f578a9fc 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -7,6 +7,7 @@
left: 0;
position: relative;
display: flex;
+ overflow: hidden;
.webBox-sideResizer {
position: absolute;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index db493934a..8be4884ce 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -11,7 +11,7 @@ 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, StopEvent, Utils } from '../../../Utils';
+import { emptyFunction, getWordAtPoint, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SnappingManager } from '../../util/SnappingManager';
@@ -29,7 +29,7 @@ import { AnchorMenu } from '../pdf/AnchorMenu';
import { Annotation } from '../pdf/Annotation';
import { SidebarAnnos } from '../SidebarAnnos';
import { StyleProp } from '../StyleProvider';
-import { DocumentViewProps } from './DocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewInternal, DocumentViewProps } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { LinkDocPreview } from './LinkDocPreview';
import { VideoBox } from './VideoBox';
@@ -54,10 +54,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
private _keyInput = React.createRef<HTMLInputElement>();
private _initialScroll: Opt<number> = NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop));
- private _getAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
private _sidebarRef = React.createRef<SidebarAnnos>();
private _searchRef = React.createRef<HTMLInputElement>();
private _searchString = '';
+
+ private get _getAnchor() {
+ return AnchorMenu.Instance?.GetAnchor;
+ }
@observable private _webUrl = ''; // url of the src parameter of the embedded iframe but not necessarily the rendered page - eg, when following a link, the rendered page changes but we don't wan the src parameter to also change as that would cause an unnecessary re-render.
@observable private _hackHide = false; // apparently changing the value of the 'sandbox' prop doesn't necessarily apply it to the active iframe. so thisforces the ifrmae to be rebuilt when allowScripts is toggled
@observable private _searching: boolean = false;
@@ -241,7 +244,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
const durationSecStr = viewTrans.match(/([0-9.]*)s/);
const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
- this.goTo(scrollTop, duration);
+ this.goTo(scrollTop, duration, 'ease');
},
{ fireImmediately: true }
);
@@ -284,18 +287,18 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }) => void) => (this._setBrushViewer = func);
brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._setBrushViewer?.(view);
- scrollFocus = (doc: Doc, smooth: boolean) => {
- if (StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl), !smooth);
+ scrollFocus = (doc: Doc, options: DocFocusOptions) => {
+ if (this._url && StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl), options.instant);
if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
- this.toggleSidebar(!smooth);
+ this.toggleSidebar(options.instant);
}
if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
if (doc !== this.rootDoc && this._outerRef.current) {
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 ? NumCast(doc.focusSpeed, 500) : 0;
- this.goTo(scrollTo, focusSpeed);
+ const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500;
+ this.goTo(scrollTo, focusSpeed, options.easeFunc);
return focusSpeed;
} else if (!this._webPageHasBeenRendered || !this.getScrollHeight() || this._initialScroll !== undefined) {
this._initialScroll = scrollTo;
@@ -305,6 +308,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
};
getAnchor = () => {
+ let ele: Opt<HTMLDivElement> = undefined;
+ try {
+ const contents = this._iframe?.contentWindow?.getSelection()?.getRangeAt(0).cloneContents();
+ if (contents) {
+ ele = document.createElement('div');
+ ele.append(contents);
+ }
+ } catch (e) {}
const anchor =
this._getAnchor(this._savedAnnotations) ??
Docs.Create.WebanchorDocument(this._url, {
@@ -312,6 +323,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
y: NumCast(this.layoutDoc._scrollTop),
unrendered: true,
});
+ anchor.text = ele?.textContent ?? '';
+ anchor.textHtml = ele?.innerHTML;
this.addDocumentWrapper(anchor);
return anchor;
};
@@ -497,11 +510,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
);
};
- goTo = (scrollTop: number, duration: number) => {
+ goTo = (scrollTop: number, duration: number, easeFunc: 'linear' | 'ease' | undefined) => {
if (this._outerRef.current) {
const iframeHeight = Math.max(scrollTop, this._scrollHeight - this.panelHeight());
if (duration) {
- smoothScroll(duration, [this._outerRef.current], scrollTop);
+ smoothScroll(duration, [this._outerRef.current], scrollTop, easeFunc);
this.setDashScrollTop(scrollTop, duration);
} else {
this.setDashScrollTop(scrollTop);
@@ -553,8 +566,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
};
static urlHash = (s: string) => {
+ const split = s.split('');
return Math.abs(
- s.split('').reduce((a: any, b: any) => {
+ split.reduce((a: any, b: any) => {
a = (a << 5) - a + b.charCodeAt(0);
return a & a;
}, 0)
@@ -686,7 +700,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
};
@action finishMarquee = (x?: number, y?: number, e?: PointerEvent) => {
- this._getAnchor = AnchorMenu.Instance?.GetAnchor;
this._marqueeing = undefined;
this._isAnnotating = false;
this._iframeClick = undefined;
@@ -836,9 +849,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
}
});
- sidebarWidth = () =>
- !this.SidebarShown ? 0 : WebBox.sidebarResizerWidth + (this._previewWidth ? WebBox.openSidebarWidth : ((NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth()) / NumCast(this.layoutDoc.nativeWidth));
-
+ sidebarWidth = () => {
+ if (!this.SidebarShown) return 0;
+ if (this._previewWidth) return WebBox.sidebarResizerWidth + WebBox.openSidebarWidth; // return default sidebar if previewing (as in viewing a link target)
+ const nativeDiff = NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc);
+ return WebBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1);
+ };
@computed get content() {
const interactive =
!this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
@@ -1000,9 +1016,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
docView={this.props.docViewPath().lastElement()}
finishMarquee={this.finishMarquee}
savedAnnotations={this.savedAnnotationsCreator}
+ selectionText={returnEmptyString}
annotationLayer={this._annotationLayer.current}
mainCont={this._mainCont.current}
- />{' '}
+ />
</div>
)}
</div>
@@ -1015,7 +1032,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}}
onPointerDown={e => this.sidebarBtnDown(e, false)}
/>
- <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.layoutDoc[WidthSym]()}%` }}>
+ <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.props.PanelWidth()}%` }}>
<SidebarAnnos
ref={this._sidebarRef}
{...this.props}
@@ -1039,5 +1056,5 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
}
ScriptingGlobals.add(function urlHash(url: string) {
- return WebBox.urlHash(url);
+ return url ? WebBox.urlHash(url) : 0;
});
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 883c4460b..e477d7ae2 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -1,7 +1,6 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
-import Color from 'color';
import { action, computed, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -12,8 +11,9 @@ import { ScriptField } from '../../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { WebField } from '../../../../fields/URLField';
import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
-import { aggregateBounds, Utils } from '../../../../Utils';
+import { aggregateBounds, StopEvent, Utils } from '../../../../Utils';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
+import { LinkManager } from '../../../util/LinkManager';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
import { SelectionManager } from '../../../util/SelectionManager';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
@@ -27,6 +27,7 @@ import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, SetAc
import { InkTranscription } from '../../InkTranscription';
import { StyleProp } from '../../StyleProvider';
import { FieldView, FieldViewProps } from '.././FieldView';
+import { OpenWhere } from '../DocumentView';
import { RichTextMenu } from '../formattedText/RichTextMenu';
import { WebBox } from '../WebBox';
import { FontIconBadge } from './FontIconBadge';
@@ -62,7 +63,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
}
showTemplate = (): void => {
const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
- dragFactory && this.props.addDocTab(dragFactory, 'add:right');
+ dragFactory && this.props.addDocTab(dragFactory, OpenWhere.addRight);
};
dragAsTemplate = (): void => {
this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
@@ -198,7 +199,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
e.preventDefault();
}}
onClick={action(() => (this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen))}>
- <input style={{ width: 30 }} className="button-input" type="number" value={checkResult} onChange={action(e => setValue(Number(e.target.value)))} />
+ <input style={{ width: 30 }} className="button-input" type="number" value={checkResult} onChange={undoBatch(action(e => setValue(Number(e.target.value))))} />
</div>
<div className={`button`} onClick={action(e => setValue(Number(checkResult) + 1))}>
<FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'plus'} />
@@ -221,7 +222,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
</div>
);
} else {
- return <div></div>;
+ return <div />;
}
}
@@ -303,7 +304,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
fontFamily: script.script.originalScript.startsWith('setFont') ? value : undefined,
backgroundColor: value === text ? Colors.LIGHT_BLUE : undefined,
}}
- onClick={() => script.script.run({ value }).result}>
+ onClick={undoBatch(() => script.script.run({ value }))}>
{value[0].toUpperCase() + value.slice(1)}
</div>
));
@@ -352,12 +353,14 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
}
colorPicker = (curColor: string) => {
- const change = (value: ColorState) => {
+ const change = (value: ColorState, ev: MouseEvent) => {
+ ev.preventDefault();
+ ev.stopPropagation();
const s = this.colorScript;
s && undoBatch(() => s.script.run({ value: Utils.colorString(value), _readOnly_: false }).result)();
};
const presets = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent'];
- return <SketchPicker onChange={change} color={curColor} presetColors={presets} />;
+ return <SketchPicker onChange={change as any /* SketchPicker passes the mouse event to the callback, but the type system doesn't know that */} color={curColor} presetColors={presets} />;
};
/**
* Color button
@@ -380,12 +383,15 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
// style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
// <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" />
// </div>;
- setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView
+ //setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView
return (
<div
className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')} ${this.colorPickerClosed}`}
style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
- onClick={action(() => (this.colorPickerClosed = !this.colorPickerClosed))}
+ onClick={action(e => {
+ this.colorPickerClosed = !this.colorPickerClosed;
+ e.stopPropagation();
+ })}
onPointerDown={e => e.stopPropagation()}>
{this.Icon(color)}
<div className="colorButton-color" style={{ backgroundColor: curColor }} />
@@ -393,7 +399,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
{/* {dropdownCaret} */}
{this.colorPickerClosed ? null : (
<div>
- <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()} onClick={e => e.stopPropagation()}>
+ <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onClick={e => e.stopPropagation()}>
{this.colorPicker(curColor)}
</div>
<div
@@ -504,8 +510,12 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
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` }}>
+ case ButtonType.TextButton:
+ // Script for checking the outcome of the toggle
+ const script = ScriptCast(this.rootDoc.script);
+ const checkResult = script?.script.run({ _readOnly_: true }).result;
+ button = (
+ <div className={`menuButton ${this.type}`} style={{ color, backgroundColor:checkResult ?? backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}>
{this.Icon(color)}
{StrCast(this.rootDoc.buttonText) ? <div className="button-text">{StrCast(this.rootDoc.buttonText)}</div> : null}
{label()}
@@ -542,11 +552,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.Views().lastElement();
+ const selected = SelectionManager.Views().lastElement()?.props.Document ?? LinkManager.currentLink;
if (checkResult) {
- return selected?.props.Document._backgroundColor ?? 'transparent';
+ return selected?._backgroundColor ?? 'transparent';
}
- if (selected) selected.props.Document._backgroundColor = color;
+ if (selected) selected._backgroundColor = color;
});
// toggle: Set overlay status of selected document
diff --git a/src/client/views/nodes/formattedText/DashDocCommentView.tsx b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
index 40dd6fbc7..fcd6e0c55 100644
--- a/src/client/views/nodes/formattedText/DashDocCommentView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
@@ -1,5 +1,5 @@
import { TextSelection } from 'prosemirror-state';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
import { Doc } from '../../../../fields/Doc';
import { DocServer } from '../../../DocServer';
import React = require('react');
@@ -9,6 +9,7 @@ import React = require('react');
// the comment can be toggled on/off with the '<-' text anchor.
export class DashDocCommentView {
dom: HTMLDivElement; // container for label and value
+ root: any;
constructor(node: any, view: any, getPos: any) {
this.dom = document.createElement('div');
@@ -30,15 +31,20 @@ export class DashDocCommentView {
e.stopPropagation();
};
- ReactDOM.render(<DashDocCommentViewInternal view={view} getPos={getPos} docid={node.attrs.docid} />, this.dom);
+ this.root = ReactDOM.createRoot(this.dom);
+ this.root.render(<DashDocCommentViewInternal view={view} getPos={getPos} docid={node.attrs.docid} />);
(this as any).dom = this.dom;
}
destroy() {
- ReactDOM.unmountComponentAtNode(this.dom);
+ this.root.unmount();
+ }
+ deselectNode() {
+ this.dom.classList.remove('ProseMirror-selectednode');
+ }
+ selectNode() {
+ this.dom.classList.add('ProseMirror-selectednode');
}
-
- selectNode() {}
}
interface IDashDocCommentViewInternal {
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 5f576be41..722d0cc4f 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -1,7 +1,7 @@
import { action, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { NodeSelection } from 'prosemirror-state';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
import { Doc, HeightSym, WidthSym } from '../../../../fields/Doc';
import { Cast, NumCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnFalse, Utils } from '../../../../Utils';
@@ -15,6 +15,7 @@ import React = require('react');
export class DashDocView {
dom: HTMLSpanElement; // container for label and value
+ root: any;
constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
this.dom = document.createElement('span');
@@ -38,14 +39,14 @@ export class DashDocView {
e.stopPropagation();
};
- ReactDOM.render(
- <DashDocViewInternal docid={node.attrs.docid} alias={node.attrs.alias} width={node.attrs.width} height={node.attrs.height} hidden={node.attrs.hidden} fieldKey={node.attrs.fieldKey} tbox={tbox} view={view} node={node} getPos={getPos} />,
- this.dom
+ this.root = ReactDOM.createRoot(this.dom);
+ this.root.render(
+ <DashDocViewInternal docid={node.attrs.docid} alias={node.attrs.alias} width={node.attrs.width} height={node.attrs.height} hidden={node.attrs.hidden} fieldKey={node.attrs.fieldKey} tbox={tbox} view={view} node={node} getPos={getPos} />
);
- (this as any).dom = this.dom;
}
destroy() {
- ReactDOM.unmountComponentAtNode(this.dom);
+ this.root.unmount();
+ // ReactDOM.unmountComponentAtNode(this.dom);
}
selectNode() {}
}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.scss b/src/client/views/nodes/formattedText/DashFieldView.scss
index c36e6804b..ad315acc8 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.scss
+++ b/src/client/views/nodes/formattedText/DashFieldView.scss
@@ -1,3 +1,5 @@
+@import '../../global/globalCssVariables';
+
.dashFieldView {
position: relative;
display: inline-flex;
@@ -22,7 +24,7 @@
position: relative;
display: inline-block;
font-weight: normal;
- background: rgba(0,0,0,0.1);
+ background: rgba(0, 0, 0, 0.1);
}
.dashFieldView-fieldSpan {
min-width: 8px;
@@ -31,11 +33,13 @@
padding-left: 2px;
display: inline-block;
background-color: rgba(155, 155, 155, 0.24);
- font-weight: bold;
span {
+ user-select: all;
min-width: 100%;
display: inline-block;
}
}
}
- \ No newline at end of file
+.ProseMirror-selectedNode {
+ outline: solid 1px $light-blue !important;
+}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 35d919f38..63347015b 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,6 +1,6 @@
import { action, computed, IReactionDisposer, observable } from 'mobx';
import { observer } from 'mobx-react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
import { DataSym, Doc, DocListCast, Field } from '../../../../fields/Doc';
import { List } from '../../../../fields/List';
import { listSpec } from '../../../../fields/Schema';
@@ -16,20 +16,19 @@ import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
import { Tooltip } from '@material-ui/core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { CollectionViewType } from '../../../documents/DocumentTypes';
+import { NodeSelection } from 'prosemirror-state';
+import { OpenWhere } from '../DocumentView';
export class DashFieldView {
dom: HTMLDivElement; // container for label and value
+ root: any;
constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
- const { boolVal, strVal } = DashFieldViewInternal.fieldContent(tbox.props.Document, tbox.rootDoc, node.attrs.fieldKey);
-
this.dom = document.createElement('div');
this.dom.style.width = node.attrs.width;
this.dom.style.height = node.attrs.height;
- this.dom.style.fontWeight = 'bold';
this.dom.style.position = 'relative';
this.dom.style.display = 'inline-block';
- this.dom.textContent = node.attrs.fieldKey.startsWith('#') ? node.attrs.fieldKey : node.attrs.fieldKey + ' ' + strVal;
this.dom.onkeypress = function (e: any) {
e.stopPropagation();
};
@@ -43,13 +42,20 @@ export class DashFieldView {
e.stopPropagation();
};
- setTimeout(() => ReactDOM.render(<DashFieldViewInternal fieldKey={node.attrs.fieldKey} docid={node.attrs.docid} width={node.attrs.width} height={node.attrs.height} hideKey={node.attrs.hideKey} tbox={tbox} />, this.dom));
- (this as any).dom = this.dom;
+ this.root = ReactDOM.createRoot(this.dom);
+ this.root.render(
+ <DashFieldViewInternal node={node} getPos={getPos} fieldKey={node.attrs.fieldKey} docid={node.attrs.docid} width={node.attrs.width} height={node.attrs.height} hideKey={node.attrs.hideKey} editable={node.attrs.editable} tbox={tbox} />
+ );
}
destroy() {
- ReactDOM.unmountComponentAtNode(this.dom);
+ this.root.unmount();
+ }
+ deselectNode() {
+ this.dom.classList.remove('ProseMirror-selectednode');
+ }
+ selectNode() {
+ this.dom.classList.add('ProseMirror-selectednode');
}
- selectNode() {}
}
interface IDashFieldViewInternal {
@@ -59,6 +65,9 @@ interface IDashFieldViewInternal {
tbox: FormattedTextBox;
width: number;
height: number;
+ editable: boolean;
+ node: any;
+ getPos: any;
}
@observer
@@ -125,7 +134,13 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
r?.addEventListener('blur', e => r && this.updateText(r.textContent!, false));
r?.addEventListener(
'pointerdown',
- action(e => e.stopPropagation())
+ action(e => {
+ // 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;
+ this.props.tbox.EditorView!.dispatch(this.props.tbox.EditorView!.state.tr.setSelection(new NodeSelection(this.props.tbox.EditorView!.state.doc.resolve(this.props.getPos()))));
+ // FormattedTextBoxComment.update(this.props.tbox, this.props.tbox.EditorView!, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc);
+ // e.stopPropagation();
+ })
);
}}>
{strVal}
@@ -138,6 +153,10 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
// we need to handle all key events on the input span or else they will propagate to prosemirror.
@action
fieldSpanKeyDown = (e: KeyboardEvent, span: HTMLSpanElement) => {
+ if (e.key === 'c' && (e.ctrlKey || e.metaKey)) {
+ navigator.clipboard.writeText(window.getSelection()?.toString() || '');
+ return;
+ }
if (e.key === 'Enter') {
// handle the enter key by "submitting" the current text to Dash's database.
this.updateText(span.textContent!, true);
@@ -153,6 +172,9 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
}
e.preventDefault(); //prevent default so that all the text in the prosemirror text box isn't selected
}
+ if (!this.props.editable) {
+ e.preventDefault();
+ }
e.stopPropagation(); // we need to handle all events or else they will propagate to prosemirror.
};
@@ -160,7 +182,6 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
updateText = (nodeText: string, forceMatch: boolean) => {
if (nodeText) {
const newText = nodeText.startsWith(':=') || nodeText.startsWith('=:=') ? ':=-computed-' : nodeText;
-
// look for a document whose id === the fieldKey being displayed. If there's a match, then that document
// holds the different enumerated values for the field in the titles of its collected documents.
// if there's a partial match from the start of the input text, complete the text --- TODO: make this an auto suggest box and select from a drop down.
@@ -207,7 +228,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, '#f1efeb'));
list.map(c => c.heading).indexOf('text') === -1 && list.push(new SchemaHeaderField('text', '#f1efeb'));
alias._pivotField = this._fieldKey.startsWith('#') ? '#' : this._fieldKey;
- this.props.tbox.props.addDocTab(alias, 'add:right');
+ this.props.tbox.props.addDocTab(alias, OpenWhere.addRight);
}
};
@@ -266,14 +287,12 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
document.addEventListener('pointerdown', hideMenu, true);
};
render() {
- const buttons = [
+ return this.getElement([
<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>
</Tooltip>,
- ];
-
- return this.getElement(buttons);
+ ]);
}
}
diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx
index 4895dcdc5..714ae458c 100644
--- a/src/client/views/nodes/formattedText/EquationView.tsx
+++ b/src/client/views/nodes/formattedText/EquationView.tsx
@@ -1,8 +1,8 @@
import EquationEditor from 'equation-editor-react';
-import { IReactionDisposer } from 'mobx';
+import { IReactionDisposer, trace } from 'mobx';
import { observer } from 'mobx-react';
import { TextSelection } from 'prosemirror-state';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
import { Doc } from '../../../../fields/Doc';
import { StrCast } from '../../../../fields/Types';
import './DashFieldView.scss';
@@ -11,8 +11,12 @@ import React = require('react');
export class EquationView {
dom: HTMLDivElement; // container for label and value
-
+ root: any;
+ tbox: FormattedTextBox;
+ view: any;
constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
+ this.tbox = tbox;
+ this.view = view;
this.dom = document.createElement('div');
this.dom.style.width = node.attrs.width;
this.dom.style.height = node.attrs.height;
@@ -22,19 +26,24 @@ export class EquationView {
e.stopPropagation();
};
- ReactDOM.render(<EquationViewInternal fieldKey={node.attrs.fieldKey} width={node.attrs.width} height={node.attrs.height} getPos={getPos} setEditor={this.setEditor} tbox={tbox} />, this.dom);
- (this as any).dom = this.dom;
+ this.root = ReactDOM.createRoot(this.dom);
+ this.root.render(<EquationViewInternal fieldKey={node.attrs.fieldKey} width={node.attrs.width} height={node.attrs.height} getPos={getPos} setEditor={this.setEditor} tbox={tbox} />);
}
_editor: EquationEditor | undefined;
setEditor = (editor?: EquationEditor) => (this._editor = editor);
destroy() {
- ReactDOM.unmountComponentAtNode(this.dom);
+ this.root.unmount();
+ // ReactDOM.unmountComponentAtNode(this.dom);
}
setSelection() {
this._editor?.mathField.focus();
}
selectNode() {
- this._editor?.mathField.focus();
+ this.tbox._applyingChange = this.tbox.fieldKey; // setting focus will make prosemirror lose focus, which will cause it to change its selection to a text selection, which causes this view to get rebuilt but it's no longer node selected, so the equationview won't have focus
+ setTimeout(() => {
+ this._editor?.mathField.focus();
+ setTimeout(() => (this.tbox._applyingChange = ''));
+ });
}
deselectNode() {}
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index d3d8c47c0..cbe0a465d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -15,7 +15,7 @@ audiotag {
position: absolute;
cursor: pointer;
border-radius: 10px;
- width: 10px;
+ width: 12px;
margin-top: -2px;
font-size: 4px;
background: lightblue;
@@ -225,6 +225,8 @@ footnote::after {
.prosemirror-attribution {
font-size: 8px;
+ float: right;
+ display: inline;
}
.footnote-tooltip::before {
@@ -740,6 +742,8 @@ footnote::after {
.prosemirror-attribution {
font-size: 8px;
+ float: right;
+ display: inline;
}
.footnote-tooltip::before {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 096f9a92c..62e215521 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1,7 +1,7 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { isEqual } from 'lodash';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx';
import { observer } from 'mobx-react';
import { baseKeymap, selectAll } from 'prosemirror-commands';
import { history } from 'prosemirror-history';
@@ -45,7 +45,7 @@ import { LightboxView } from '../../LightboxView';
import { AnchorMenu } from '../../pdf/AnchorMenu';
import { SidebarAnnos } from '../../SidebarAnnos';
import { StyleProp } from '../../StyleProvider';
-import { DocumentViewInternal } from '../DocumentView';
+import { DocFocusOptions, DocumentViewInternal, OpenWhere } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
import { LinkDocPreview } from '../LinkDocPreview';
import { DashDocCommentView } from './DashDocCommentView';
@@ -87,7 +87,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
private _ref: React.RefObject<HTMLDivElement> = React.createRef();
private _scrollRef: React.RefObject<HTMLDivElement> = React.createRef();
private _editorView: Opt<EditorView>;
- private _applyingChange: string = '';
+ public _applyingChange: string = '';
private _searchIndex = 0;
private _lastTimedMark: Mark | undefined = undefined;
private _cachedLinks: Doc[] = [];
@@ -231,7 +231,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
}
- getAnchor = () => this.makeLinkAnchor(undefined, 'add:right', undefined, 'Anchored Selection');
+ getAnchor = () => this.makeLinkAnchor(undefined, 'add:right', undefined, 'Anchored Selection', false);
@action
setupAnchorMenu = () => {
@@ -239,7 +239,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
AnchorMenu.Instance.OnClick = (e: PointerEvent) => {
!this.layoutDoc.showSidebar && this.toggleSidebar();
- this._sidebarRef.current?.anchorMenuClick(this.getAnchor());
+ setTimeout(() => this._sidebarRef.current?.anchorMenuClick(this.makeLinkAnchor(undefined, 'add:right', undefined, 'Anchored Selection', true))); // give time for sidebarRef to be created
};
AnchorMenu.Instance.OnAudio = (e: PointerEvent) => {
!this.layoutDoc.showSidebar && this.toggleSidebar();
@@ -393,13 +393,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
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 isNodeSel = this._editorView.state.selection instanceof NodeSelection;
const f = this._editorView.state.selection.from;
const t = this._editorView.state.selection.to;
var tr = this._editorView.state.tr as any;
const autoAnch = this._editorView.state.schema.marks.autoLinkAnchor;
tr = tr.removeMark(0, tr.doc.content.size, autoAnch);
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)));
+ tr = tr.setSelection(isNodeSel && false ? new NodeSelection(tr.doc.resolve(f)) : 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);
@@ -498,12 +499,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const end = this._editorView.state.doc.nodeSize - 2;
this._editorView.dispatch(this._editorView.state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark));
}
- if (FormattedTextBox.PasteOnLoad) {
- const pdfDocId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfOrigin');
- const pdfRegionId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfRegion');
- FormattedTextBox.PasteOnLoad = undefined;
- setTimeout(() => pdfDocId && pdfRegionId && this.addPdfReference(pdfDocId, pdfRegionId, undefined), 10);
- }
};
adoptAnnotation = (start: number, end: number, mark: Mark) => {
const view = this._editorView!;
@@ -691,12 +686,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
};
@undoBatch
- pinToPres = (anchor: Doc) => this.props.pinToPres(anchor);
+ pinToPres = (anchor: Doc) => this.props.pinToPres(anchor, {});
@undoBatch
- makePushpin = (anchor: Doc) => (anchor.isPushpin = !anchor.isPushpin);
+ makePushpin = (anchor: Doc) => (anchor.followLinkToggle = !anchor.followLinkToggle);
- isPushpin = (anchor: Doc) => BoolCast(anchor.isPushpin);
+ isPushpin = (anchor: Doc) => BoolCast(anchor.followLinkToggle);
specificContextMenu = (e: React.MouseEvent): void => {
const cm = ContextMenu.Instance;
@@ -896,9 +891,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
};
// TODO: nda -- Look at how link anchors are added
- makeLinkAnchor(anchorDoc?: Doc, location?: string, targetHref?: string, title?: string) {
+ makeLinkAnchor(anchorDoc?: Doc, location?: string, targetHref?: string, title?: string, noPreview?: boolean) {
const state = this._editorView?.state;
if (state) {
+ let selectedText = '';
const sel = state.selection;
const splitter = state.schema.marks.splitter.create({ id: Utils.GenerateGuid() });
let tr = state.tr.addMark(sel.from, sel.to, splitter);
@@ -910,13 +906,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
const allAnchors = [{ href, title, anchorId: anchor[Id] }];
allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.linkAnchor.name)?.attrs.allAnchors ?? []));
- const link = state.schema.marks.linkAnchor.create({ allAnchors, title, location });
+ const link = state.schema.marks.linkAnchor.create({ allAnchors, title, location, noPreview });
tr = tr.addMark(pos, pos + node.nodeSize, link);
+ selectedText += (node as Node).textContent;
}
});
this.dataDoc[ForceServerWrite] = this.dataDoc[UpdatingFromServer] = true; // need to allow permissions for adding links to readonly/augment only documents
this._editorView!.dispatch(tr.removeMark(sel.from, sel.to, splitter));
this.dataDoc[UpdatingFromServer] = this.dataDoc[ForceServerWrite] = false;
+ anchor.text = selectedText;
return anchor;
}
return anchorDoc ?? this.rootDoc;
@@ -924,10 +922,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
return anchorDoc ?? this.rootDoc;
}
- scrollFocus = (textAnchor: Doc, smooth: boolean) => {
+ scrollFocus = (textAnchor: Doc, options: DocFocusOptions) => {
let didToggle = false;
if (DocListCast(this.Document[this.fieldKey + '-sidebar']).includes(textAnchor) && !this.SidebarShown) {
- this.toggleSidebar(!smooth);
+ this.toggleSidebar(options.instant);
didToggle = true;
}
const textAnchorId = textAnchor[Id];
@@ -968,7 +966,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const content = (ret.frag as any)?.content;
if ((ret.frag.size > 2 || (content?.length && content[0].type === this._editorView.state.schema.nodes.audiotag)) && ret.start >= 0) {
- smooth && (this._focusSpeed = 500);
+ !options.instant && (this._focusSpeed = 500);
let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start
if (ret.frag.firstChild) {
selection = TextSelection.between(editor.state.doc.resolve(ret.start), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected
@@ -1122,7 +1120,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const durationSecStr = viewTrans.match(/([0-9.]*)s/);
const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
if (duration) {
- smoothScroll(duration, this._scrollRef.current, Math.abs(pos || 0));
+ this._scrollStopper = smoothScroll(duration, this._scrollRef.current, Math.abs(pos || 0), 'ease', this._scrollStopper);
} else {
this._scrollRef.current.scrollTo({ top: pos });
}
@@ -1132,6 +1130,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
);
quickScroll = undefined;
this.tryUpdateScrollHeight();
+ setTimeout(this.tryUpdateScrollHeight, 250);
}
pushToGoogleDoc = async () => {
@@ -1233,61 +1232,38 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
};
handlePaste = (view: EditorView, event: Event, slice: Slice): boolean => {
- const cbe = event as ClipboardEvent;
- const pdfDocId = cbe.clipboardData?.getData('dash/pdfOrigin');
- const pdfRegionId = cbe.clipboardData?.getData('dash/pdfRegion');
- return pdfDocId && pdfRegionId && this.addPdfReference(pdfDocId, pdfRegionId, slice) ? true : false;
+ const pdfAnchorId = (event as ClipboardEvent).clipboardData?.getData('dash/pdfAnchor');
+ return pdfAnchorId && this.addPdfReference(pdfAnchorId) ? true : false;
};
- addPdfReference = (pdfDocId: string, pdfRegionId: string, slice?: Slice) => {
+ addPdfReference = (pdfAnchorId: string) => {
const view = this._editorView!;
- if (pdfDocId && pdfRegionId) {
- DocServer.GetRefField(pdfDocId).then(pdfDoc => {
- DocServer.GetRefField(pdfRegionId).then(pdfRegion => {
- if (pdfDoc instanceof Doc && pdfRegion instanceof Doc) {
- setTimeout(async () => {
- const targetField = Doc.LayoutFieldKey(pdfDoc);
- const targetAnnotations = await DocListCastAsync(pdfDoc[DataSym][targetField + '-annotations']); // bcz: better to have the PDF's view handle updating its own annotations
- if (targetAnnotations) targetAnnotations.push(pdfRegion);
- else Doc.AddDocToList(pdfDoc[DataSym], targetField + '-annotations', pdfRegion);
- });
-
- const link = DocUtils.MakeLink({ doc: this.rootDoc }, { doc: pdfRegion }, 'PDF pasted');
- if (link) {
- const linkId = link[Id];
- const quote = view.state.schema.nodes.blockquote.create({ content: addMarkToFrag(slice?.content || view.state.doc.content, (node: Node) => addLinkMark(node, StrCast(pdfDoc.title), linkId)) });
- const newSlice = new Slice(Fragment.from(quote), slice?.openStart || 0, slice?.openEnd || 0);
- if (slice) {
- view.dispatch(view.state.tr.replaceSelection(newSlice).scrollIntoView().setMeta('paste', true).setMeta('uiEvent', 'paste'));
- } else {
- selectAll(view.state, (tx: Transaction) => view.dispatch(tx.replaceSelection(newSlice).scrollIntoView()));
- }
- }
+ if (pdfAnchorId) {
+ DocServer.GetRefField(pdfAnchorId).then(pdfAnchor => {
+ if (pdfAnchor instanceof Doc) {
+ const dashField = view.state.schema.nodes.paragraph.create({}, [
+ view.state.schema.nodes.dashField.create({ fieldKey: 'text', docid: pdfAnchor[Id], hideKey: true, editable: false }, undefined, [
+ view.state.schema.marks.linkAnchor.create({
+ allAnchors: [{ href: `/doc/${this.rootDoc[Id]}`, title: this.rootDoc.title, anchorId: `${this.rootDoc[Id]}` }],
+ location: 'add:right',
+ title: `from: ${DocCast(pdfAnchor.context).title}`,
+ noPreview: true,
+ docref: false,
+ }),
+ view.state.schema.marks.pFontSize.create({ fontSize: '8px' }),
+ view.state.schema.marks.em.create({}),
+ ]),
+ ]);
+
+ const link = DocUtils.MakeLink({ doc: pdfAnchor }, { doc: this.rootDoc }, 'PDF pasted');
+ if (link) {
+ view.dispatch(view.state.tr.replaceSelectionWith(dashField, false).scrollIntoView().setMeta('paste', true).setMeta('uiEvent', 'paste'));
}
- });
+ }
});
return true;
}
return false;
-
- function addMarkToFrag(frag: Fragment, marker: (node: Node) => Node) {
- const nodes: Node[] = [];
- frag.forEach(node => nodes.push(marker(node)));
- return Fragment.fromArray(nodes);
- }
-
- function addLinkMark(node: Node, title: string, linkId: string) {
- if (!node.isText) {
- const content = addMarkToFrag(node.content, (node: Node) => addLinkMark(node, title, linkId));
- return node.copy(content);
- }
- const marks = [...node.marks];
- const linkIndex = marks.findIndex(mark => mark.type.name === 'link');
- const allLinks = [{ href: Doc.globalServerPath(linkId), title, linkId }];
- const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: 'add:right', title, docref: true });
- marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link);
- return node.mark(marks);
- }
};
isActiveTab(el: Element | null | undefined) {
@@ -1308,6 +1284,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
});
}
_didScroll = false;
+ _scrollStopper: undefined | (() => void);
setupEditor(config: any, fieldKey: string) {
const curText = Cast(this.dataDoc[this.fieldKey], RichTextField, null) || StrCast(this.dataDoc[this.fieldKey]);
const rtfField = Cast((!curText && this.layoutDoc[this.fieldKey]) || this.dataDoc[fieldKey], RichTextField);
@@ -1326,7 +1303,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const shift = Math.min(topOff ?? Number.MAX_VALUE, botOff ?? Number.MAX_VALUE);
const scrollPos = scrollRef.scrollTop + shift * self.props.ScreenToLocalTransform().Scale;
if (this._focusSpeed !== undefined) {
- scrollPos && smoothScroll(this._focusSpeed, scrollRef, scrollPos);
+ scrollPos && (this._scrollStopper = smoothScroll(this._focusSpeed, scrollRef, scrollPos, 'ease', this._scrollStopper));
} else {
scrollRef.scrollTo({ top: scrollPos });
}
@@ -1397,11 +1374,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
} else if (this._editorView) {
this._editorView.dispatch(this._editorView.state.tr.addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
}
- FormattedTextBox.DontSelectInitialText = false;
}
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) {
+ const tr = this._editorView.state.tr;
+ const { from, to } = tr.selection;
+ // for some reason, the selection is sometimes lost in the sidebar view when prosemirror syncs the seledtion with the dom, so reset the selectoin after the document has ben fully instantiated.
+ if (FormattedTextBox.DontSelectInitialText) setTimeout(() => this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(from), tr.doc.resolve(to)))), 250);
this._editorView.state.storedMarks = [
...(this._editorView.state.storedMarks ?? []),
...(!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) })] : []),
@@ -1412,7 +1392,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontSize) })] : []),
...(Doc.UserDoc().fontWeight === 'bold' ? [schema.mark(schema.marks.strong)] : []),
];
+ if (FormattedTextBox.PasteOnLoad) {
+ const pdfAnchorId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfAnchor');
+ FormattedTextBox.PasteOnLoad = undefined;
+ pdfAnchorId && this.addPdfReference(pdfAnchorId);
+ }
}
+ FormattedTextBox.DontSelectInitialText = false;
}
componentWillUnmount() {
@@ -1443,7 +1429,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const func = () => {
const docView = DocumentManager.Instance.getDocumentView(audiodoc);
if (!docView) {
- this.props.addDocTab(audiodoc, 'add:bottom');
+ this.props.addDocTab(audiodoc, OpenWhere.addBottom);
setTimeout(func);
} else docView.ComponentView?.playFrom?.(timecode, Cast(anchor.timecodeToHide, 'number', null)); // bcz: would be nice to find the next audio tag in the doc and play until that
};
@@ -1480,7 +1466,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
document.removeEventListener('pointermove', this.onSelectMove);
};
onPointerUp = (e: React.PointerEvent): void => {
- if (!this._editorView?.state.selection.empty && FormattedTextBox._canAnnotate && !(e.nativeEvent as any).dash) this.setupAnchorMenu();
+ if (!this._editorView?.state.selection.empty && !(this._editorView?.state.selection instanceof NodeSelection) && FormattedTextBox._canAnnotate && !(e.nativeEvent as any).dash) this.setupAnchorMenu();
if (!this._downEvent) return;
this._downEvent = false;
if (this.props.isContentActive(true) && !(e.nativeEvent as any).dash) {
@@ -1489,7 +1475,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
!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; // 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);
+ FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true');
+ if (target) return;
}
if (e.button === 0 && this.props.isSelected(true) && !e.altKey) {
@@ -1524,6 +1511,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
//applyDevTools.applyDevTools(this._editorView);
FormattedTextBox.Focused = this;
this.ProseRef?.children[0] === e.nativeEvent.target && this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props);
+ this.startUndoTypingBatch();
};
@observable public static Focused: FormattedTextBox | undefined;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index bdf59863b..e7ca26d5c 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -1,5 +1,5 @@
import { Mark, ResolvedPos } from 'prosemirror-model';
-import { EditorState } from 'prosemirror-state';
+import { EditorState, NodeSelection } from 'prosemirror-state';
import { EditorView } from 'prosemirror-view';
import { Doc } from '../../../../fields/Doc';
import { DocServer } from '../../../DocServer';
@@ -92,7 +92,7 @@ export class FormattedTextBoxComment {
FormattedTextBoxComment.tooltip.style.display = '';
}
- static update(textBox: FormattedTextBox, view: EditorView, lastState?: EditorState, hrefs: string = '', linkDoc: string = '') {
+ static update(textBox: FormattedTextBox, view: EditorView, lastState?: EditorState, hrefs: string = '', linkDoc: string = '', noPreview: boolean = false) {
FormattedTextBoxComment.textBox = textBox;
if (hrefs || !lastState?.doc.eq(view.state.doc) || !lastState?.selection.eq(view.state.selection)) {
FormattedTextBoxComment.setupPreview(
@@ -102,12 +102,13 @@ export class FormattedTextBoxComment {
?.trim()
.split(' ')
.filter(h => h),
- linkDoc
+ linkDoc,
+ noPreview
);
}
}
- static setupPreview(view: EditorView, textBox: FormattedTextBox, hrefs?: string[], linkDoc?: string) {
+ static setupPreview(view: EditorView, textBox: FormattedTextBox, hrefs?: string[], linkDoc?: string, noPreview?: boolean) {
const state = view.state;
// this section checks to see if the insertion point is over text entered by a different user. If so, it sets ths comment text to indicate the user and the modification date
if (state.selection.$from) {
@@ -130,8 +131,8 @@ export class FormattedTextBoxComment {
if (state.selection.$from && hrefs?.length) {
const nbef = findStartOfMark(state.selection.$from, view, findLinkMark);
const naft = findEndOfMark(state.selection.$from, view, findLinkMark) || nbef;
- nbef &&
- naft &&
+ //nbef &&
+ naft &&
LinkDocPreview.SetLinkInfo({
docProps: textBox.props,
linkSrc: textBox.rootDoc,
@@ -139,6 +140,7 @@ export class FormattedTextBoxComment {
location: (pos => [pos.left, pos.top + 25])(view.coordsAtPos(state.selection.from - Math.max(0, nbef - 1))),
hrefs,
showHeader: true,
+ noPreview,
});
}
}
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 3d9bd6add..68b0488a2 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -9,6 +9,7 @@ import { GetEffectiveAcl } from '../../../../fields/util';
import { Utils } from '../../../../Utils';
import { Docs } from '../../../documents/Documents';
import { SelectionManager } from '../../../util/SelectionManager';
+import { OpenWhere } from '../DocumentView';
import { liftListItem, sinkListItem } from './prosemirrorPatches.js';
const mac = typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false;
@@ -135,7 +136,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
//Command to create a new Tab with a PDF of all the command shortcuts
bind('Mod-/', (state: EditorState, dispatch: (tx: Transaction) => void) => {
const newDoc = Docs.Create.PdfDocument(Utils.prepend('/assets/cheat-sheet.pdf'), { _width: 300, _height: 300 });
- props.addDocTab(newDoc, 'add:right');
+ props.addDocTab(newDoc, OpenWhere.addRight);
});
//Commands to modify BlockType
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 47833dd43..e3c67ad2e 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -248,8 +248,7 @@ export class RichTextRules {
// [[fieldKey:Doc]] => show field of doc
new InputRule(new RegExp(/\[\[([a-zA-Z_\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@:\.\? \-0-9]+)?\]\]$/), (state, match, start, end) => {
const fieldKey = match[1];
- const rawdocid = match[3];
- const docid = rawdocid ? normalizeEmail(!rawdocid.includes('@') ? Doc.CurrentUserEmail + rawdocid : rawdocid.substring(1)) : undefined;
+ const docid = match[3]?.replace(':', '');
const value = match[2]?.substring(1);
if (!fieldKey) {
if (docid) {
@@ -259,7 +258,7 @@ export class RichTextRules {
if (rstate) {
this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3))));
}
- const target = (docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: rawdocid.replace(/^:/, ''), _width: 500, _height: 500 }, docid);
+ const target = (docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500 }, docid);
DocUtils.MakeLink({ doc: this.TextBox.getAnchor() }, { doc: target }, 'portal to:portal from', undefined);
const fstate = this.TextBox.EditorView?.state;
@@ -275,7 +274,7 @@ export class RichTextRules {
const num = value.match(/^[0-9.]$/);
this.Document[DataSym][fieldKey] = value === 'true' ? true : value === 'false' ? false : num ? Number(value) : value;
}
- const fieldView = state.schema.nodes.dashField.create({ fieldKey, docid });
+ const fieldView = state.schema.nodes.dashField.create({ fieldKey, docid, hideKey: false });
return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
}),
diff --git a/src/client/views/nodes/formattedText/SummaryView.tsx b/src/client/views/nodes/formattedText/SummaryView.tsx
index 01acc3de9..4e75d374c 100644
--- a/src/client/views/nodes/formattedText/SummaryView.tsx
+++ b/src/client/views/nodes/formattedText/SummaryView.tsx
@@ -1,6 +1,6 @@
import { TextSelection } from 'prosemirror-state';
import { Fragment, Node, Slice } from 'prosemirror-model';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
import React = require('react');
// an elidable textblock that collapses when its '<-' is clicked and expands when its '...' anchor is clicked.
@@ -9,6 +9,7 @@ import React = require('react');
// method instead of changing prosemirror's text when the expand/elide buttons are clicked.
export class SummaryView {
dom: HTMLSpanElement; // container for label and value
+ root: any;
constructor(node: any, view: any, getPos: any) {
const self = this;
@@ -35,13 +36,14 @@ export class SummaryView {
return js.apply(this, arguments);
};
- ReactDOM.render(<SummaryViewInternal />, this.dom);
- (this as any).dom = this.dom;
+ this.root = ReactDOM.createRoot(this.dom);
+ this.root.render(<SummaryViewInternal />);
}
className = (visible: boolean) => 'formattedTextBox-summarizer' + (visible ? '' : '-collapsed');
destroy() {
- ReactDOM.unmountComponentAtNode(this.dom);
+ this.root.unmount();
+ // ReactDOM.unmountComponentAtNode(this.dom);
}
selectNode() {}
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 00c41e187..8d43c33f0 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -75,6 +75,7 @@ export const marks: { [index: string]: MarkSpec } = {
allAnchors: { default: [] as { href: string; title: string; anchorId: string }[] },
location: { default: null },
title: { default: null },
+ noPreview: { default: false },
docref: { default: false }, // flags whether the linked text comes from a document within Dash. If so, an attribution label is appended after the text
},
inclusive: false,
@@ -85,6 +86,7 @@ export const marks: { [index: string]: MarkSpec } = {
return {
location: dom.getAttribute('location'),
title: dom.getAttribute('title'),
+ noPreview: dom.getAttribute('noPreview'),
};
},
},
@@ -95,12 +97,9 @@ export const marks: { [index: string]: MarkSpec } = {
return node.attrs.docref && node.attrs.title
? [
'div',
- ['span', `"`],
['span', 0],
- ['span', `"`],
- ['br'],
[
- 'a',
+ 'span',
{
...node.attrs,
class: 'prosemirror-attribution',
@@ -108,19 +107,8 @@ export const marks: { [index: string]: MarkSpec } = {
},
node.attrs.title,
],
- ['br'],
]
- : //node.attrs.allLinks.length === 1 ?
- ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, location: node.attrs.location, style: `text-decoration: underline` }, 0];
- // ["div", { class: "prosemirror-anchor" },
- // ["span", { class: "prosemirror-linkBtn" },
- // ["a", { ...node.attrs, class: linkids, "data-targetids": targetids, title: `${node.attrs.title}` }, 0],
- // ["input", { class: "prosemirror-hrefoptions" }],
- // ],
- // ["div", { class: "prosemirror-links" }, ...node.attrs.allLinks.map((item: { href: string, title: string }) =>
- // ["a", { class: "prosemirror-dropdownlink", href: item.href }, item.title]
- // )]
- // ];
+ : ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, 'data-noPreview': node.attrs.noPreview, location: node.attrs.location, style: `text-decoration: underline` }, 0];
},
},
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 66d747bf7..aa2475dca 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -263,6 +263,7 @@ export const nodes: { [index: string]: NodeSpec } = {
fieldKey: { default: '' },
docid: { default: '' },
hideKey: { default: false },
+ editable: { default: true },
},
group: 'inline',
draggable: false,
diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss
index 4d60a02f1..fd202590e 100644
--- a/src/client/views/nodes/trails/PresBox.scss
+++ b/src/client/views/nodes/trails/PresBox.scss
@@ -938,14 +938,19 @@
min-width: 15px;
max-width: 100px;
left: 8px;
+ margin: auto;
+ margin-left: unset;
+ height: 100%;
}
.presBox-presentPanel {
display: flex;
justify-self: end;
width: 100%;
- max-width: 300px;
- min-width: 150px;
+ margin: auto;
+ margin-right: unset;
+ height: 100%;
+ position: relative;
}
select {
@@ -955,7 +960,7 @@
.presBox-button {
cursor: pointer;
- height: 25px;
+ //height: 100%;
border-radius: 5px;
display: none;
justify-content: center;
@@ -986,6 +991,9 @@
width: max-content;
position: absolute;
right: 10px;
+ margin: auto;
+ margin-right: unset;
+ height: 100%;
.present-icon {
margin-right: 7px;
@@ -999,9 +1007,16 @@
position: relative;
display: inline-block;
left: 8px;
+ margin: auto;
+ margin-left: unset;
}
}
+.presBox-buttons.inOverlay {
+ padding-top: unset;
+ padding-bottom: unset;
+}
+
.presBox-backward,
.presBox-forward {
width: 25px;
@@ -1052,6 +1067,8 @@
font-size: 30px;
position: absolute;
min-width: 50px;
+ margin: auto;
+ margin-left: unset;
}
}
@@ -1087,13 +1104,12 @@
color: $white;
border-radius: 5px;
grid-template-rows: 100%;
- height: 25;
+ height: 100%;
width: max-content;
min-width: max-content;
justify-content: space-evenly;
align-items: center;
display: flex;
- position: absolute;
transition: all 0.2s;
.presPanel-button-text {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 4e8aed8a6..855a7f171 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1,18 +1,18 @@
import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
-import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableSet, observe, 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, FieldResult, StrListCast } from '../../../../fields/Doc';
+import { AnimationSym, Doc, DocListCast, FieldResult, HighlightSym, Opt, StrListCast } from '../../../../fields/Doc';
import { Copy, Id } 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, StopEvent } from '../../../../Utils';
+import { AudioField } from '../../../../fields/URLField';
+import { emptyFunction, emptyPath, returnFalse, returnOne, setupMoveUpEvents, StopEvent } from '../../../../Utils';
import { DocServer } from '../../../DocServer';
import { Docs } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
@@ -22,25 +22,40 @@ import { SelectionManager } from '../../../util/SelectionManager';
import { SettingsManager } from '../../../util/SettingsManager';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
import { CollectionDockingView } from '../../collections/CollectionDockingView';
-import { CollectionFreeFormView, MarqueeViewBounds } from '../../collections/collectionFreeForm';
+import { CollectionFreeFormView, computeTimelineLayout, MarqueeViewBounds } from '../../collections/collectionFreeForm';
import { CollectionView } from '../../collections/CollectionView';
import { TabDocView } from '../../collections/TabDocView';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { Colors } from '../../global/globalEnums';
import { LightboxView } from '../../LightboxView';
import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentView';
+import { DocFocusOptions, DocumentView, OpenWhere, OpenWhereMod } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
import { ScriptingBox } from '../ScriptingBox';
import './PresBox.scss';
-import { PresEffect, PresMovement, PresStatus } from './PresEnums';
+import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
+import { CollectionStackedTimeline } from '../../collections/CollectionStackedTimeline';
+const { Howl } = require('howler');
export interface PinProps {
audioRange?: boolean;
activeFrame?: number;
+ currentFrame?: number;
hidePresBox?: boolean;
pinViewport?: MarqueeViewBounds; // pin a specific viewport on a freeform view (use MarqueeView.CurViewBounds to compute if no region has been selected)
pinDocLayout?: boolean; // pin layout info (width/height/x/y)
pinDocContent?: boolean; // pin data info (scroll/pan/zoom/text)
+ pinAudioPlay?: boolean; // pin audio annotation
+ pinData?: {
+ scrollable?: boolean | undefined;
+ pannable?: boolean | undefined;
+ temporal?: boolean | undefined;
+ clippable?: boolean | undefined;
+ dataview?: boolean | undefined;
+ textview?: boolean | undefined;
+ poslayoutview?: boolean | undefined;
+ dataannos?: boolean | undefined;
+ };
}
@observer
@@ -49,39 +64,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
return FieldView.LayoutString(PresBox, fieldKey);
}
- /**
- * returns an entrance animation effect function to wrap a JSX element
- * @param presEffectDoc presentation effects document that specifies the animation effect parameters
- * @returns a function that will wrap a JSX animation element wrapping any JSX element
- */
- public static AnimationEffect(renderDoc: JSX.Element, presEffectDoc: Doc) {
- const effectProps = {
- left: presEffectDoc.presEffectDirection === PresEffect.Left,
- right: presEffectDoc.presEffectDirection === PresEffect.Right,
- top: presEffectDoc.presEffectDirection === PresEffect.Top,
- bottom: presEffectDoc.presEffectDirection === PresEffect.Bottom,
- opposite: true,
- delay: NumCast(presEffectDoc.presTransition),
- };
- //prettier-ignore
- switch (StrCast(presEffectDoc.presEffect)) {
- 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>;
- }
- }
-
private _disposers: { [name: string]: IReactionDisposer } = {};
public selectedArray = new ObservableSet<Doc>();
@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;
@@ -93,7 +79,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@observable _expandBoolean: 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;
@@ -125,7 +110,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
return Cast(this.activeItem?.presentationTargetDoc, Doc, null);
}
@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;
+ if ([DocumentType.PDF, DocumentType.WEB, DocumentType.RTF].includes(this.targetDoc.type as DocumentType) || this.targetDoc._viewType === CollectionViewType.Stacking) return true;
return false;
}
@computed get panable() {
@@ -151,6 +136,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@action
componentWillUnmount() {
this._unmounting = true;
+ if (this._presTimer) clearTimeout(this._presTimer);
document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
this.resetPresentation();
// Turn of progressivize editors
@@ -158,7 +144,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
Object.values(this._disposers).forEach(disposer => disposer?.());
}
- @action
componentDidMount() {
this._disposers.keyboard = reaction(
() => this.selectedDoc,
@@ -182,10 +167,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
);
this.props.setContentView?.(this);
this._unmounting = false;
- this.rootDoc._forceRenderEngine = 'timeline';
- this.layoutDoc.presStatus = PresStatus.Edit;
- this.layoutDoc._gridGap = 0;
- this.layoutDoc._yMargin = 0;
+ if (this.props.renderDepth > 0) {
+ runInAction(() => {
+ this.rootDoc._forceRenderEngine = computeTimelineLayout.name;
+ this.layoutDoc._gridGap = 0;
+ this.layoutDoc._yMargin = 0;
+ });
+ }
this.turnOffEdit(true);
this._disposers.selection = reaction(
() => SelectionManager.Views(),
@@ -199,17 +187,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
PresBox.Instance = this;
};
- // There are still other internal frames and should go through all frames before going to next slide
- nextInternalFrame = (targetDoc: Doc, activeItem: Doc) => {
- const currentFrame = Cast(targetDoc?._currentFrame, 'number', null);
- const childDocs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
- targetDoc._viewTransition = 'all 1s';
- setTimeout(() => (targetDoc._viewTransition = undefined), 1010);
- this.nextKeyframe(targetDoc, activeItem);
- if (activeItem.presProgressivize) CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, targetDoc);
- else targetDoc.keyFrameEditing = true;
- };
-
_mediaTimer!: [NodeJS.Timeout, Doc];
// 'Play on next' for audio or video therefore first navigate to the audio/video before it should be played
startTempMedia = (targetDoc: Doc, activeItem: Doc) => {
@@ -232,38 +209,53 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
// TODO: to handle child slides (entering into subtrail and exiting), also the next() and back() functions
// No more frames in current doc and next slide is defined, therefore move to next slide
nextSlide = (slideNum?: number) => {
- let nextSelected = slideNum ?? this.itemIndex + 1;
- this.gotoDocument(nextSelected, this.activeItem);
- for (nextSelected = nextSelected + 1; nextSelected < this.childDocs.length; nextSelected++) {
- if (this.childDocs[nextSelected].groupWithUp) {
- this.gotoDocument(nextSelected, this.activeItem, true);
- } else {
- break;
+ const nextSlideInd = slideNum ?? this.itemIndex + 1;
+ let curSlideInd = nextSlideInd;
+ const resetSelection = action(() => {
+ this.clearSelectedArray();
+ for (let i = nextSlideInd; i <= curSlideInd; i++) {
+ this.addToSelectedArray(this.childDocs[i]);
}
- }
+ });
+ CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.());
+ this.clearSelectedArray();
+ const doGroupWithUp =
+ (nextSelected: number, force = false) =>
+ () => {
+ if (nextSelected < this.childDocs.length) {
+ if (force || this.childDocs[nextSelected].groupWithUp) {
+ const serial = nextSelected + 1 < this.childDocs.length && NumCast(this.childDocs[nextSelected + 1].groupWithUp) > 1;
+ if (serial) {
+ this.gotoDocument(nextSelected, this.activeItem, true, async () => {
+ const waitTime = NumCast(this.activeItem.presDuration) - NumCast(this.activeItem.presTransition);
+ await new Promise<void>(res => setTimeout(() => res(), Math.max(0, waitTime)));
+ doGroupWithUp(nextSelected + 1)();
+ });
+ } else {
+ this.gotoDocument(nextSelected, this.activeItem, undefined, resetSelection);
+ curSlideInd = this.itemIndex;
+ doGroupWithUp(nextSelected + 1)();
+ }
+ }
+ }
+ };
+ doGroupWithUp(curSlideInd, true)();
};
// Called when the user activates 'next' - to move to the next part of the pres. trail
@action
next = () => {
- const activeNext = Cast(this.childDocs[this.itemIndex + 1], Doc, null);
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
- const lastFrame = Cast(targetDoc?.lastFrame, 'number', null);
- const curFrame = NumCast(targetDoc?._currentFrame);
- let internalFrames: boolean = false;
- if (activeItem.presProgressivize || activeItem.zoomProgressivize || targetDoc.scrollProgressivize) internalFrames = true;
- if (internalFrames && lastFrame !== undefined && curFrame < lastFrame) {
- // Case 1: There are still other frames and should go through all frames before going to next slide
- this.nextInternalFrame(targetDoc, activeItem);
- } else if (this.childDocs[this.itemIndex + 1] !== undefined) {
- // Case 2: No more frames in current doc and next slide is defined, therefore move to next slide
+ if (this.childDocs[this.itemIndex + 1] !== undefined) {
+ // Case 1: No more frames in current doc and next slide is defined, therefore move to next slide
const slides = DocListCast(this.rootDoc[StrCast(this.presFieldKey, 'data')]);
const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d)))) : this.itemIndex;
- this.nextSlide(curLast + 1);
- } else if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presStatus === PresStatus.Edit)) {
- // Case 3: Last slide and presLoop is toggled ON or it is in Edit mode
- this.nextSlide(0);
+ this.nextSlide(curLast + 1 === this.childDocs.length ? (this.layoutDoc.presLoop ? 0 : curLast) : curLast + 1);
+ } else {
+ if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presStatus === PresStatus.Edit)) {
+ // Case 2: Last slide and presLoop is toggled ON or it is in Edit mode
+ this.nextSlide(0);
+ }
+ return 0;
}
return this.itemIndex;
};
@@ -272,29 +264,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@action
back = () => {
const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
const prevItem = Cast(this.childDocs[Math.max(0, this.itemIndex - 1)], Doc, null);
- const prevTargetDoc = Cast(prevItem.presentationTargetDoc, Doc, null);
- const lastFrame = Cast(targetDoc.lastFrame, 'number', null);
- const curFrame = NumCast(targetDoc._currentFrame);
let prevSelected = this.itemIndex;
// Functionality for group with up
let didZoom = activeItem.presMovement;
for (; prevSelected > 0 && this.childDocs[Math.max(0, prevSelected - 1)].groupWithUp; prevSelected--) {
didZoom = didZoom === 'none' ? this.childDocs[prevSelected].presMovement : didZoom;
}
- if (lastFrame !== undefined && curFrame >= 1) {
- // Case 1: There are still other frames and should go through all frames before going to previous slide
- this.prevKeyframe(targetDoc, activeItem);
- } else if (activeItem && this.childDocs[this.itemIndex - 1] !== undefined) {
+ if (activeItem && this.childDocs[this.itemIndex - 1] !== undefined) {
// Case 2: There are no other frames so it should go to the previous slide
prevSelected = Math.max(0, prevSelected - 1);
this.nextSlide(prevSelected);
this.rootDoc._itemIndex = prevSelected;
- if (NumCast(prevTargetDoc.lastFrame) > 0) prevTargetDoc._currentFrame = NumCast(prevTargetDoc.lastFrame);
} else if (this.childDocs[this.itemIndex - 1] === undefined && this.layoutDoc.presLoop) {
// Case 3: Pres loop is on so it should go to the last slide
- this.gotoDocument(this.childDocs.length - 1, activeItem);
+ this.nextSlide(this.childDocs.length - 1);
}
return this.itemIndex;
};
@@ -302,21 +286,22 @@ 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) => {
+ public gotoDocument = action((index: number, from?: Doc, group?: boolean, finished?: () => void) => {
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 activeFrame = activeItem.presActiveFrame ?? activeItem.presCurrentFrame;
+ if (activeFrame !== undefined) {
const transTime = NumCast(activeItem.presTransition, 500);
- const context = DocCast(DocCast(activeItem.presentationTargetDoc).context);
+ const acontext = activeItem.presActiveFrame !== undefined ? DocCast(DocCast(activeItem.presentationTargetDoc).context) : DocCast(activeItem.presentationTargetDoc);
+ const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext).annotationOn) : acontext;
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);
+ const ffview = DocumentManager.Instance.getFirstDocumentView(context)?.ComponentView as CollectionFreeFormView;
+ if (ffview?.childDocs) {
+ this._keyTimer = CollectionFreeFormDocumentView.gotoKeyframe(this._keyTimer, ffview.childDocs.slice(), transTime);
+ context._currentFrame = NumCast(activeFrame);
}
}
}
@@ -330,56 +315,91 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (this.layoutDoc.presStatus !== PresStatus.Edit && (targetDoc.type === DocumentType.AUDIO || targetDoc.type === DocumentType.VID) && activeItem.mediaStart === 'auto') {
this.startTempMedia(targetDoc, activeItem);
}
- if (targetDoc) {
- Doc.linkFollowHighlight(targetDoc.annotationOn instanceof Doc ? [targetDoc, targetDoc.annotationOn] : targetDoc);
- 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.clearSelectedArray();
this.childDocs[index] && this.addToSelectedArray(this.childDocs[index]); //Update selected array
this.turnOffEdit();
- this.navigateToActiveItem(); //Handles movement to element only when presTrail is list
+ this.navigateToActiveItem(finished); //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, DocumentType.COL].includes(target.type as any) && target.activeFrame === undefined;
- const poslayoutview = [DocumentType.COL].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, poslayoutview };
+ static pinDataTypes(target?: Doc): { scrollable?: boolean; pannable?: boolean; temporal?: boolean; clippable?: boolean; dataview?: boolean; textview?: boolean; poslayoutview?: boolean; dataannos?: boolean } {
+ const targetType = target?.type as any;
+ const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetType) || target?._viewType === CollectionViewType.Stacking;
+ const pannable = [DocumentType.IMG, DocumentType.PDF].includes(targetType) || (targetType === DocumentType.COL && target?._viewType === CollectionViewType.Freeform);
+ const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(targetType);
+ const clippable = [DocumentType.COMPARISON].includes(targetType);
+ const dataview = [DocumentType.INK, DocumentType.COL, DocumentType.IMG].includes(targetType) && target?.activeFrame === undefined;
+ const poslayoutview = [DocumentType.COL].includes(targetType) && target?.activeFrame === undefined;
+ const textview = [DocumentType.RTF].includes(targetType) && target?.activeFrame === undefined;
+ const dataannos = false;
+ return { scrollable, pannable, temporal, clippable, dataview, textview, poslayoutview, dataannos };
}
@action
- static restoreTargetDocView(bestTarget: Doc, activeItem: Doc) {
- const transTime = NumCast(activeItem.presTransition, 500);
- const presTransitionTime = `all ${transTime}ms`;
- const { scrollable, pannable, temporal, clippable, dataview, textview, poslayoutview } = this.pinDataTypes(bestTarget);
- bestTarget._viewTransition = presTransitionTime;
- if (clippable) bestTarget._clipWidth = activeItem.presPinClipWidth;
- if (temporal) bestTarget._currentTimecode = activeItem.presStartTime;
- if (scrollable) {
- bestTarget._scrollTop = activeItem.presPinViewScroll;
- const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
- if (contentBounds) {
- const dv = DocumentManager.Instance.getDocumentView(bestTarget)?.ComponentView;
- dv?.brushView?.({ panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] });
+ playAnnotation = (anno: AudioField) => {};
+ @action
+ static restoreTargetDocView(bestTargetView: Opt<DocumentView>, pinProps: PinProps | undefined, activeItem: Doc, transTime: number, pinDataTypes = this.pinDataTypes(bestTargetView?.rootDoc)) {
+ if (!bestTargetView) return;
+ const bestTarget = bestTargetView.rootDoc;
+ let changed = false;
+ if (pinProps?.pinDocLayout) {
+ if (
+ bestTarget.x !== NumCast(activeItem.presX, NumCast(bestTarget.x)) ||
+ bestTarget.y !== NumCast(activeItem.presY, NumCast(bestTarget.y)) ||
+ bestTarget.rotation !== NumCast(activeItem.presRot, NumCast(bestTarget.rotation)) ||
+ bestTarget.width !== NumCast(activeItem.presWidth, NumCast(bestTarget.width)) ||
+ bestTarget.height !== NumCast(activeItem.presHeight, NumCast(bestTarget.height))
+ ) {
+ bestTarget._dataTransition = `all ${transTime}ms`;
+ bestTarget.x = NumCast(activeItem.presX, NumCast(bestTarget.x));
+ bestTarget.y = NumCast(activeItem.presY, NumCast(bestTarget.y));
+ bestTarget.rotation = NumCast(activeItem.presRot, NumCast(bestTarget.rotation));
+ bestTarget.width = NumCast(activeItem.presWidth, NumCast(bestTarget.width));
+ bestTarget.height = NumCast(activeItem.presHeight, NumCast(bestTarget.height));
+ setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
+ changed = true;
+ }
+ }
+ if (pinDataTypes.clippable) {
+ if (bestTarget._clipWidth !== activeItem.presPinClipWidth) {
+ bestTarget._clipWidth = activeItem.presPinClipWidth;
+ changed = true;
+ }
+ }
+ if (pinDataTypes.temporal) {
+ if (bestTarget._currentTimecode !== activeItem.presStartTime) {
+ bestTarget._currentTimecode = activeItem.presStartTime;
+ changed = true;
}
}
- if (dataview) Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
- if (textview) Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
- if (poslayoutview) {
+ if (pinDataTypes.scrollable) {
+ if (bestTarget._scrollTop !== activeItem.presPinViewScroll) {
+ bestTarget._scrollTop = activeItem.presPinViewScroll;
+ changed = true;
+ const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
+ if (contentBounds) {
+ const dv = DocumentManager.Instance.getDocumentView(bestTarget)?.ComponentView;
+ dv?.brushView?.({ panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] });
+ }
+ }
+ }
+ if (pinDataTypes.dataannos) {
+ const fkey = Doc.LayoutFieldKey(bestTarget);
+ Doc.GetProto(bestTarget)[fkey + '-annotations'] = new List<Doc>([...DocListCast(bestTarget[fkey + '-annotations']).filter(doc => doc.unrendered), ...DocListCast(activeItem.presAnnotations)]);
+ }
+ if (pinDataTypes.dataview && activeItem.presData !== undefined) {
+ const fkey = Doc.LayoutFieldKey(bestTarget);
+ Doc.GetProto(bestTarget)[fkey] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
+ bestTarget[fkey + '-useAlt'] = activeItem.presUseAlt;
+ }
+ if (pinDataTypes.textview && activeItem.presData !== undefined) Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
+ if (pinDataTypes.poslayoutview) {
+ changed = true;
StrListCast(activeItem.presPinLayoutData)
.map(str => JSON.parse(str) as { id: string; x: number; y: number; w: number; h: number })
.forEach(data => {
const doc = DocServer.GetCachedRefField(data.id) as Doc;
- doc._dataTransition = presTransitionTime;
+ doc._dataTransition = `all ${transTime}ms`;
doc.x = data.x;
doc.y = data.y;
doc._width = data.w;
@@ -389,16 +409,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
() =>
StrListCast(activeItem.presPinLayoutData)
.map(str => JSON.parse(str) as { id: string; x: number; y: number; w: number; h: number })
- .forEach(
- action(data => {
- const doc = DocServer.GetCachedRefField(data.id) as Doc;
- doc._dataTransition = undefined;
- })
- ),
+ .forEach(action(data => ((DocServer.GetCachedRefField(data.id) as Doc)._dataTransition = undefined))),
transTime + 10
);
}
- if (pannable) {
+ if (pinDataTypes.pannable) {
const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
if (contentBounds) {
const viewport = { panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] };
@@ -407,25 +422,29 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const dv = DocumentManager.Instance.getDocumentView(bestTarget);
if (dv) {
const computedScale = NumCast(activeItem.presZoom, 1) * Math.min(dv.props.PanelWidth() / viewport.width, dv.props.PanelHeight() / viewport.height);
- activeItem.presMovement === 'zoom' && (bestTarget._viewScale = computedScale);
+ activeItem.presMovement === PresMovement.Zoom && (bestTarget._viewScale = computedScale);
dv.ComponentView?.brushView?.(viewport);
}
} else {
- bestTarget._panX = activeItem.presPinViewX;
- bestTarget._panY = activeItem.presPinViewY;
- activeItem.presMovement === 'zoom' && (bestTarget._viewScale = activeItem.presPinViewScale);
+ if (bestTarget._panX !== activeItem.presPinViewX || bestTarget._panY !== activeItem.presPinViewY || bestTarget._viewScale !== activeItem.presPinViewScale) {
+ bestTarget._panX = activeItem.presPinViewX;
+ bestTarget._panY = activeItem.presPinViewY;
+ bestTarget._viewScale = activeItem.presPinViewScale;
+ changed = true;
+ }
}
}
- return setTimeout(() => (bestTarget._viewTransition = undefined), transTime + 10);
+ if (changed) {
+ return bestTargetView.setViewTransition('all', transTime);
+ }
}
/// 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) {
- const { scrollable, pannable, temporal, clippable, dataview, textview, poslayoutview } = this.pinDataTypes(pinDoc);
- if (pinProps?.pinDocLayout) {
+ static pinDocView(pinDoc: Doc, pinProps: PinProps, targetDoc: Doc) {
+ if (pinProps.pinDocLayout) {
pinDoc.presPinLayout = true;
pinDoc.presX = NumCast(targetDoc.x);
pinDoc.presY = NumCast(targetDoc.y);
@@ -433,36 +452,51 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
pinDoc.presWidth = NumCast(targetDoc.width);
pinDoc.presHeight = NumCast(targetDoc.height);
}
- if (pinProps?.pinDocContent) {
- pinDoc.presPinData = scrollable || temporal || pannable || clippable || dataview || textview || poslayoutview || pinProps.activeFrame !== undefined;
- if (dataview) pinDoc.presData = targetDoc[Doc.LayoutFieldKey(targetDoc)] instanceof ObjectField ? (targetDoc[Doc.LayoutFieldKey(targetDoc)] as ObjectField)[Copy]() : targetDoc.data;
- if (textview) pinDoc.presData = targetDoc[Doc.LayoutFieldKey(targetDoc)] instanceof ObjectField ? (targetDoc[Doc.LayoutFieldKey(targetDoc)] as ObjectField)[Copy]() : targetDoc.text;
- if (scrollable) pinDoc.presPinViewScroll = pinDoc._scrollTop;
- if (clippable) pinDoc.presPinClipWidth = pinDoc._clipWidth;
- if (poslayoutview) pinDoc.presPinLayoutData = new List<string>(DocListCast(pinDoc.presData).map(d => JSON.stringify({ id: d[Id], x: NumCast(d.x), y: NumCast(d.y), w: NumCast(d._width), h: NumCast(d._height) })));
- if (pannable) {
- pinDoc.presPinViewX = NumCast(pinDoc._panX);
- pinDoc.presPinViewY = NumCast(pinDoc._panY);
- pinDoc.presPinViewScale = NumCast(pinDoc._viewScale, 1);
+ if (pinProps.pinAudioPlay) pinDoc.followLinkAudio = true;
+ if (pinProps.pinData) {
+ pinDoc.presPinData =
+ pinProps.pinData.scrollable ||
+ pinProps.pinData.temporal ||
+ pinProps.pinData.pannable ||
+ pinProps.pinData.clippable ||
+ pinProps.pinData.dataview ||
+ pinProps.pinData.textview ||
+ pinProps.pinData.poslayoutview ||
+ pinProps?.activeFrame !== undefined;
+ if (pinProps.pinData.dataview) {
+ const fkey = Doc.LayoutFieldKey(targetDoc);
+ pinDoc.presUseAlt = targetDoc[fkey + '-useAlt'];
+ pinDoc.presData = targetDoc[fkey] instanceof ObjectField ? (targetDoc[fkey] as ObjectField)[Copy]() : targetDoc.data;
+ }
+ if (pinProps.pinData.dataannos) {
+ const fkey = Doc.LayoutFieldKey(targetDoc);
+ pinDoc.presAnnotations = new List<Doc>(DocListCast(Doc.GetProto(targetDoc)[fkey + '-annotations']).filter(doc => !doc.unrendered));
}
- if (temporal) {
- pinDoc.presStartTime = pinDoc._currentTimecode;
- const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], NumCast(pinDoc.presStartTime) + 0.1);
- pinDoc.presEndTime = NumCast(pinDoc.clipEnd, duration);
+ if (pinProps.pinData.textview) pinDoc.presData = targetDoc[Doc.LayoutFieldKey(targetDoc)] instanceof ObjectField ? (targetDoc[Doc.LayoutFieldKey(targetDoc)] as ObjectField)[Copy]() : targetDoc.text;
+ if (pinProps.pinData.scrollable) pinDoc.presPinViewScroll = targetDoc._scrollTop;
+ if (pinProps.pinData.clippable) pinDoc.presPinClipWidth = targetDoc._clipWidth;
+ if (pinProps.pinData.poslayoutview) pinDoc.presPinLayoutData = new List<string>(DocListCast(targetDoc.presData).map(d => JSON.stringify({ id: d[Id], x: NumCast(d.x), y: NumCast(d.y), w: NumCast(d._width), h: NumCast(d._height) })));
+ if (pinProps.pinData.pannable) {
+ pinDoc.presPinViewX = NumCast(targetDoc._panX);
+ pinDoc.presPinViewY = NumCast(targetDoc._panY);
+ pinDoc.presPinViewScale = NumCast(targetDoc._viewScale, 1);
+ }
+ if (pinProps.pinData.temporal) {
+ pinDoc.presStartTime = targetDoc._currentTimecode;
+ const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], NumCast(targetDoc.presStartTime) + 0.1);
+ pinDoc.presEndTime = NumCast(targetDoc.clipEnd, duration);
}
}
if (pinProps?.pinViewport) {
// If pinWithView option set then update scale and x / y props of slide
const bounds = pinProps.pinViewport;
pinDoc.presPinView = true;
- pinDoc.presPinViewScale = NumCast(pinDoc._viewScale, 1);
+ pinDoc.presPinViewScale = NumCast(targetDoc._viewScale, 1);
pinDoc.presPinViewX = bounds.left + bounds.width / 2;
pinDoc.presPinViewY = bounds.top + bounds.height / 2;
pinDoc.presPinViewBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
}
}
-
- static _navTimer: NodeJS.Timeout;
/**
* This method makes sure that cursor navigates to the element that
* has the option open and last in the group.
@@ -471,127 +505,106 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
* a new tab. If presCollection is undefined it will open the document
* on the right.
*/
- navigateToActiveItem = () => {
+ navigateToActiveItem = (afterNav?: () => void) => {
const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
+ const finished = () => {
+ afterNav?.();
+ console.log('Finish Slide Nav: ' + targetDoc.title);
+ targetDoc[AnimationSym] = undefined;
+ };
const srcContext = Cast(targetDoc.context, Doc, null) ?? Cast(Cast(targetDoc.annotationOn, Doc, null)?.context, Doc, null);
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 = CollectionDockingView.Instance && Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === srcContext);
+ const includesDoc = () => (DocumentManager.Instance.getDocumentView(targetDoc) ? true : false); // DocListCast(presCollection?.data).includes(targetDoc);
+ const tabMap = CollectionDockingView.Instance?.tabMap;
+ const tab = tabMap && Array.from(tabMap).find(tab => tab.DashDoc === srcContext || tab.DashDoc === targetDoc);
// Handles the setting of presCollection
- if (includesDoc) {
+ if (includesDoc()) {
//Case 1: Pres collection should not change as it is already the same
} else if (tab !== undefined) {
// Case 2: Pres collection should update
this.layoutDoc.presCollection = srcContext;
}
- const presStatus = this.rootDoc.presStatus;
const selViewCache = Array.from(this.selectedArray);
const dragViewCache = Array.from(this._dragArray);
const eleViewCache = Array.from(this._eleArray);
- const self = this;
const resetSelection = action(() => {
- const presDocView = DocumentManager.Instance.getDocumentView(self.rootDoc);
- if (presDocView) SelectionManager.SelectView(presDocView, false);
- self.rootDoc.presStatus = presStatus;
- self.clearSelectedArray();
- selViewCache.forEach(doc => self.addToSelectedArray(doc));
- self._dragArray.splice(0, self._dragArray.length, ...dragViewCache);
- self._eleArray.splice(0, self._eleArray.length, ...eleViewCache);
+ if (!includesDoc()) {
+ const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc);
+ if (presDocView) SelectionManager.SelectView(presDocView, false);
+ this.clearSelectedArray();
+ selViewCache.forEach(doc => this.addToSelectedArray(doc));
+ this._dragArray.splice(0, this._dragArray.length, ...dragViewCache);
+ this._eleArray.splice(0, this._eleArray.length, ...eleViewCache);
+ }
+ finished();
});
- const openInTab = (doc: Doc, finished?: () => void) => {
- (collectionDocView ?? this).props.addDocTab(doc, '');
+ const createDocView = (doc: Doc, finished?: () => void) => {
+ DocumentManager.Instance.AddViewRenderedCb(doc, () => finished?.());
+ (collectionDocView ?? this).props.addDocTab(doc, OpenWhere.lightbox);
this.layoutDoc.presCollection = targetDoc;
- // this still needs some fixing
- setTimeout(resetSelection, 500);
- if (doc !== targetDoc) {
- setTimeout(finished ?? emptyFunction, 100); /// give it some time to create the targetDoc if we're opening up its context
- } else {
- finished?.();
- }
};
- PresBox.NavigateToTarget(targetDoc, activeItem, openInTab, srcContext, includesDoc || tab ? undefined : resetSelection);
+ PresBox.NavigateToTarget(targetDoc, activeItem, createDocView, srcContext, includesDoc() || tab ? finished : resetSelection);
};
- static NavigateToTarget(targetDoc: Doc, activeItem: Doc, openInTab: any, srcContext: Doc, finished?: () => void) {
- if ((activeItem.presPinLayout || 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.rotation = NumCast(activeItem.presRot, NumCast(targetDoc.rotation));
- targetDoc.width = NumCast(activeItem.presWidth, NumCast(targetDoc.width));
- targetDoc.height = NumCast(activeItem.presHeight, NumCast(targetDoc.height));
- setTimeout(() => (targetDoc._dataTransition = undefined), transTime + 10);
+ static NavigateToTarget(targetDoc: Doc, activeItem: Doc, createDocView: any, srcContext: Doc, finished?: () => void) {
+ if (activeItem.presMovement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) {
+ (DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.();
+ return;
}
+ const restoreLayout = () => {
+ // 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.
+ const pinDocLayout = (BoolCast(activeItem.presPinLayout) || BoolCast(activeItem.presPinView)) && DocCast(targetDoc.context)?._currentFrame === undefined;
+ if (activeItem.presPinData || activeItem.presPinView || pinDocLayout) {
+ // targetDoc may or may not be displayed. so get the first available document (or alias) view that matches targetDoc and use it
+ PresBox.restoreTargetDocView(DocumentManager.Instance.getFirstDocumentView(targetDoc), { pinDocLayout }, activeItem, NumCast(activeItem.presTransition, 500));
+ }
+ };
// If openDocument is selected then it should open the document for the user
if (activeItem.openDocument) {
LightboxView.SetLightboxDoc(targetDoc); // openInTab(targetDoc);
- } else if (targetDoc && activeItem.presMovement !== PresMovement.None) {
- LightboxView.SetLightboxDoc(undefined);
- const zooming = activeItem.presMovement !== PresMovement.Pan;
- DocumentManager.Instance.jumpToDocument(targetDoc, zooming, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, finished, undefined, true, NumCast(activeItem.presZoom, 1));
- } else if (activeItem.presMovement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) {
- (DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.();
- }
- // 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.presPinData || activeItem.presPinView) {
- clearTimeout(PresBox._navTimer);
- // targetDoc may or may not be displayed. this gets the first available document (or alias) view that matches targetDoc
- const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
- if (bestTarget) PresBox._navTimer = PresBox.restoreTargetDocView(bestTarget, activeItem);
+ setTimeout(restoreLayout);
+ } else {
+ if (targetDoc) {
+ const options: DocFocusOptions = {
+ willPan: activeItem.presMovement !== PresMovement.None,
+ willPanZoom: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center,
+ zoomScale: activeItem.presMovement === PresMovement.Center ? 0 : NumCast(activeItem.presZoom, 1),
+ zoomTime: activeItem.presMovement === PresMovement.Jump ? 0 : NumCast(activeItem.presTransition, 500),
+ effect: activeItem,
+ noSelect: true,
+ originatingDoc: activeItem,
+ easeFunc: StrCast(activeItem.presEaseFunc, 'ease') as any,
+ zoomTextSelections: true,
+ };
+ if (activeItem.presentationTargetDoc instanceof Doc) activeItem.presentationTargetDoc[AnimationSym] = undefined;
+ var containerDocContext = srcContext ? [srcContext] : [];
+ while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) {
+ containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
+ }
+ const testTarget = containerDocContext.length ? containerDocContext[0] : targetDoc;
+ if (LightboxView.LightboxDoc && !DocumentManager.Instance.getLightboxDocumentView(testTarget)) {
+ DocumentManager.Instance.AddViewRenderedCb(LightboxView.LightboxDoc, dv => {
+ if (LightboxView.LightboxDoc && !DocumentManager.Instance.getLightboxDocumentView(LightboxView.LightboxDoc)) {
+ DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finished);
+ restoreLayout();
+ } else {
+ LightboxView.SetLightboxDoc(undefined);
+ DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finished);
+ restoreLayout();
+ }
+ });
+ return;
+ }
+ DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finished);
+ restoreLayout();
+ } else restoreLayout();
}
}
/**
- * Uses the viewfinder to progressivize through the different views of a single collection.
- * @param activeItem: document for which internal zoom is used
- */
- zoomProgressivizeNext = (activeItem: Doc) => {
- const targetDoc: Doc = this.targetDoc;
- const srcContext = Cast(targetDoc?.context, Doc, null);
- const docView = DocumentManager.Instance.getDocumentView(targetDoc);
- const vfLeft = this.checkList(targetDoc, activeItem['viewfinder-left-indexed']);
- const vfWidth = this.checkList(targetDoc, activeItem['viewfinder-width-indexed']);
- const vfTop = this.checkList(targetDoc, activeItem['viewfinder-top-indexed']);
- const vfHeight = this.checkList(targetDoc, activeItem['viewfinder-height-indexed']);
- // Case 1: document that is not a Golden Layout tab
- if (srcContext) {
- const srcDocView = DocumentManager.Instance.getDocumentView(srcContext);
- if (srcDocView) {
- const layoutdoc = Doc.Layout(targetDoc);
- const panelWidth: number = srcDocView.props.PanelWidth();
- const panelHeight: number = srcDocView.props.PanelHeight();
- const newPanX = NumCast(targetDoc.x) + NumCast(layoutdoc._width) / 2;
- const newPanY = NumCast(targetDoc.y) + NumCast(layoutdoc._height) / 2;
- const newScale = 0.9 * Math.min(Number(panelWidth) / vfWidth, Number(panelHeight) / vfHeight);
- srcContext._panX = newPanX + (vfLeft + vfWidth / 2);
- srcContext._panY = newPanY + (vfTop + vfHeight / 2);
- srcContext._viewScale = newScale;
- }
- }
- // Case 2: document is the containing collection
- if (docView && !srcContext) {
- const panelWidth: number = docView.props.PanelWidth();
- const panelHeight: number = docView.props.PanelHeight();
- const newScale = 0.9 * Math.min(Number(panelWidth) / vfWidth, Number(panelHeight) / vfHeight);
- targetDoc._panX = vfLeft + vfWidth / 2;
- targetDoc._panY = vfTop + vfWidth / 2;
- targetDoc._viewScale = newScale;
- }
- const resize = document.getElementById('resizable');
- if (resize) {
- resize.style.width = vfWidth + 'px';
- resize.style.height = vfHeight + 'px';
- resize.style.top = vfTop + 'px';
- resize.style.left = vfLeft + 'px';
- }
- };
-
- /**
* For 'Hide Before' and 'Hide After' buttons making sure that
* they are hidden each time the presentation is updated.
*/
@@ -600,54 +613,57 @@ 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;
const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc);
- const curInd: number = itemIndexes.indexOf(index);
if (tagDoc === this.layoutDoc.presCollection) {
tagDoc.opacity = 1;
} else {
- if (curDoc.presHideBefore) {
- if (itemIndexes.length > 1 && curInd !== 0) {
+ if (curDoc.presHide) {
+ if (index !== this.itemIndex) {
tagDoc.opacity = 1;
- } else {
- if (index > this.itemIndex) {
- tagDoc.opacity = 0;
- } else if (index === this.itemIndex || !curDoc.presHideAfter) {
- tagDoc.opacity = 1;
- }
}
}
- if (curDoc.presHideAfter) {
- if (itemIndexes.length > 1 && curInd !== itemIndexes.length - 1) {
+ const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex);
+ if (curDoc.presHideBefore && index === hidingIndBef) {
+ if (index > this.itemIndex) {
+ tagDoc.opacity = 0;
+ } else if (index === this.itemIndex || !curDoc.presHideAfter) {
tagDoc.opacity = 1;
- } else {
- if (index < this.itemIndex) {
- tagDoc.opacity = 0;
- } else if (index === this.itemIndex || !curDoc.presHideBefore) {
- tagDoc.opacity = 1;
- }
+ }
+ }
+ const hidingIndAft = itemIndexes
+ .slice()
+ .reverse()
+ .find(item => item < this.itemIndex);
+ if (curDoc.presHideAfter && index === hidingIndAft) {
+ if (index < this.itemIndex) {
+ tagDoc.opacity = 0;
+ } else if (index === this.itemIndex || !curDoc.presHideBefore) {
+ tagDoc.opacity = 1;
+ }
+ }
+ const hidingInd = itemIndexes.find(item => item === this.itemIndex);
+ if (curDoc.presHide && index === hidingInd) {
+ if (index === this.itemIndex) {
+ tagDoc.opacity = 0;
}
}
}
});
};
- //The function that starts or resets presentaton functionally, depending on presStatus of the layoutDoc
- @action
- startAutoPres = (startSlide: number) => {
- this.layoutDoc.presStatus = PresStatus.Autoplay;
- this.startPresentation(startSlide);
- clearTimeout(this._presTimer);
- const func = (itemIndex: number) => {
- if (itemIndex === this.next()) this.layoutDoc.presStatus = PresStatus.Manual;
- else
- this._presTimer = setTimeout(
- () => this.layoutDoc.presStatus !== PresStatus.Manual && func(this.itemIndex),
- NumCast(this.activeItem.presDuration, this.activeItem.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem.presTransition)
- );
+ _exitTrail: Opt<() => void>;
+ PlayTrail = (doc: Doc) => {
+ const savedState = { c: doc, x: NumCast(doc.panX), y: NumCast(doc.panY), s: NumCast(doc.viewScale) };
+ this.startPresentation(0);
+ this._exitTrail = () => {
+ const { x, y, s, c } = savedState;
+ c._panX = x;
+ c._panY = y;
+ c._viewScale = s;
+ LightboxView.SetLightboxDoc(undefined);
+ Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc);
+ return PresStatus.Edit;
};
-
- func(this.itemIndex);
};
// The function pauses the auto presentation
@@ -676,14 +692,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
// The function allows for viewing the pres path on toggle
- @action togglePath = (srcContext: Doc, off?: boolean) => {
- if (off) {
- this._pathBoolean = false;
- srcContext.presPathView = false;
- } else {
- runInAction(() => (this._pathBoolean = !this._pathBoolean));
- srcContext.presPathView = this._pathBoolean;
- }
+ @action togglePath = (off?: boolean) => {
+ this._pathBoolean = off ? false : !this._pathBoolean;
+ CollectionFreeFormView.ShowPresPaths = this._pathBoolean;
};
// The function allows for expanding the view of pres on toggle
@@ -701,40 +712,54 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
* @param startIndex: index that the presentation will start at
*/
startPresentation = (startIndex: number) => {
- this.childDocs.forEach(doc => {
- const tagDoc = doc.presentationTargetDoc as Doc;
- if (doc.presHideBefore && this.childDocs.indexOf(doc) > startIndex) {
- tagDoc.opacity = 0;
- }
- if (doc.presHideAfter && this.childDocs.indexOf(doc) < startIndex) {
- tagDoc.opacity = 0;
- }
- });
- this.gotoDocument(startIndex, this.activeItem);
+ clearTimeout(this._presTimer);
+ if (this.childDocs.length) {
+ this.layoutDoc.presStatus = PresStatus.Autoplay;
+ this.childDocs.forEach(doc => {
+ const tagDoc = doc.presentationTargetDoc as Doc;
+ if (doc.presHideBefore && this.childDocs.indexOf(doc) > startIndex) tagDoc.opacity = 0;
+ if (doc.presHideAfter && this.childDocs.indexOf(doc) < startIndex) tagDoc.opacity = 0;
+ // if (doc.presHide && this.childDocs.indexOf(doc) === startIndex) tagDoc.opacity = 0;
+ });
+ const func = () => {
+ const delay = NumCast(this.activeItem.presDuration, this.activeItem.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem.presTransition);
+ this._presTimer = setTimeout(() => {
+ if (!this.next()) this.layoutDoc.presStatus = this._exitTrail?.() ?? PresStatus.Manual;
+ this.layoutDoc.presStatus === PresStatus.Autoplay && func();
+ }, delay);
+ };
+ this.gotoDocument(startIndex, this.activeItem, undefined, func);
+ }
};
/**
* The method called to open the presentation as a minimized view
*/
@action
- updateMinimize = async () => {
+ enterMinimize = () => {
+ clearTimeout(this._presTimer);
+ const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+ this.props.removeDocument?.(this.layoutDoc);
+ return PresBox.OpenPresMinimized(this.rootDoc, [pt[0] + (this.props.PanelWidth() - 250), pt[1] + 10]);
+ };
+ exitMinimize = () => {
if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
- this.layoutDoc.presStatus = PresStatus.Edit;
Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc);
- CollectionDockingView.AddSplit(this.rootDoc, 'right');
- } else {
- this.layoutDoc.presStatus = PresStatus.Edit;
- clearTimeout(this._presTimer);
- const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
- this.rootDoc.overlayX = pt[0] + (this.props.PanelWidth() - 250);
- this.rootDoc.overlayY = pt[1] + 10;
- this.rootDoc._height = 30;
- this.rootDoc._width = 248;
- Doc.AddDocToList(Doc.MyOverlayDocs, undefined, this.rootDoc);
- this.props.removeDocument?.(this.layoutDoc);
+ CollectionDockingView.AddSplit(this.rootDoc, OpenWhereMod.right);
}
+ return PresStatus.Edit;
};
+ public static minimizedWidth = 198;
+ public static OpenPresMinimized(doc: Doc, pt: number[]) {
+ doc.overlayX = pt[0];
+ doc.overlayY = pt[1];
+ doc._height = 30;
+ doc._width = PresBox.minimizedWidth;
+ Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc);
+ return (doc.presStatus = PresStatus.Manual);
+ }
+
/**
* Called when the user changes the view type
* Either 'List' (stacking) or 'Slides' (carousel)
@@ -766,20 +791,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (this.childDocs[stopDocIndex - 1].mediaStopTriggerList) {
const list = DocListCast(this.childDocs[stopDocIndex - 1].mediaStopTriggerList);
list.push(activeItem);
- // this.childDocs[stopDocIndex - 1].mediaStopTriggerList = list;
- console.log(list);
+ // this.childDocs[stopDocIndex - 1].mediaStopTriggerList = list;\
} else {
this.childDocs[stopDocIndex - 1].mediaStopTriggerList = new List<Doc>();
const list = DocListCast(this.childDocs[stopDocIndex - 1].mediaStopTriggerList);
list.push(activeItem);
// this.childDocs[stopDocIndex - 1].mediaStopTriggerList = list;
- console.log(list);
}
});
movementName = action((activeItem: Doc) => {
- if (![PresMovement.Zoom, PresMovement.Pan, PresMovement.Jump, PresMovement.None].includes(StrCast(activeItem.presMovement) as any)) {
- activeItem.presMovement = 'zoom';
+ if (![PresMovement.Zoom, PresMovement.Pan, PresMovement.Center, PresMovement.Jump, PresMovement.None].includes(StrCast(activeItem.presMovement) as any)) {
+ return PresMovement.Zoom;
}
return StrCast(activeItem.presMovement);
});
@@ -804,7 +827,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
} else {
if (!doc.aliasOf) {
const original = Doc.MakeAlias(doc);
- TabDocView.PinDoc(original);
+ TabDocView.PinDoc(original, {});
setTimeout(() => this.removeDocument(doc), 0);
return false;
} else {
@@ -868,6 +891,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
//Regular click
@action
selectElement = async (doc: Doc) => {
+ CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.());
this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(DocCast(doc.context)), 0);
else this.updateCurrentPresentation(DocCast(doc.context));
@@ -920,7 +944,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
@@ -947,11 +971,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
break;
case 'Escape':
if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
- this.updateMinimize();
- } else if (this.layoutDoc.presStatus === 'edit') {
+ this.exitClicked();
+ } else if (this.layoutDoc.presStatus === PresStatus.Edit) {
this.clearSelectedArray();
this._eleArray.length = this._dragArray.length = 0;
- } else this.layoutDoc.presStatus = 'edit';
+ } else {
+ this.layoutDoc.presStatus = PresStatus.Edit;
+ }
if (this._presTimer) clearTimeout(this._presTimer);
handled = true;
break;
@@ -1010,17 +1036,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
};
- /**
- *
- */
- @action
- viewPaths = () => {
- const srcContext = Cast(this.rootDoc.presCollection, Doc, null);
- if (srcContext) {
- this.togglePath(srcContext);
- }
- };
-
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
@@ -1100,16 +1115,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
*/
@computed get paths() {
let pathPoints = '';
- const presCollection = Cast(this.rootDoc.presCollection, Doc, null);
this.childDocs.forEach((doc, index) => {
const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
- const srcContext = Cast(tagDoc?.context, Doc, null);
- if (tagDoc && presCollection === srcContext) {
+ if (tagDoc) {
const n1x = NumCast(tagDoc.x) + NumCast(tagDoc._width) / 2;
const n1y = NumCast(tagDoc.y) + NumCast(tagDoc._height) / 2;
if ((index = 0)) pathPoints = n1x + ',' + n1y;
else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
- } else if (doc.presPinView && presCollection === tagDoc) {
+ } else if (doc.presPinView) {
const n1x = NumCast(doc.presPinViewX);
const n1y = NumCast(doc.presPinViewY);
if ((index = 0)) pathPoints = n1x + ',' + n1y;
@@ -1133,14 +1146,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
/>
);
}
+ getPaths = (collection: Doc) => this.paths; // needs to be smarter and figure out the paths to draw for this specific collection. or better yet, draw everything in an overlay layer instad of within a collection
// Converts seconds to ms and updates presTransition
- setTransitionTime = (number: String, change?: number) => {
+ public static SetTransitionTime = (number: String, setter: (timeInMS: number) => void, change?: number) => {
let timeInMS = Number(number) * 1000;
if (change) timeInMS += change;
if (timeInMS < 100) timeInMS = 100;
- if (timeInMS > 10000) timeInMS = 10000;
- this.selectedArray.forEach(doc => (doc.presTransition = timeInMS));
+ if (timeInMS > 100000) timeInMS = 100000;
+ setter(timeInMS);
+ };
+ setTransitionTime = (number: String, change?: number) => {
+ PresBox.SetTransitionTime(number, (timeInMS: number) => this.selectedArray.forEach(doc => (doc.presTransition = timeInMS)), change);
};
// Converts seconds to ms and updates presTransition
@@ -1176,6 +1193,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@undoBatch
@action
+ updateHide = (activeItem: Doc) => {
+ activeItem.presHide = !activeItem.presHide;
+ this.selectedArray.forEach(doc => (doc.presHide = activeItem.presHide));
+ };
+
+ @undoBatch
+ @action
updateHideAfter = (activeItem: Doc) => {
activeItem.presHideAfter = !activeItem.presHideAfter;
this.selectedArray.forEach(doc => (doc.presHideAfter = activeItem.presHideAfter));
@@ -1187,10 +1211,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
activeItem.openDocument = !activeItem.openDocument;
this.selectedArray.forEach(doc => (doc.openDocument = activeItem.openDocument));
};
+ @undoBatch
+ @action
+ updateEaseFunc = (activeItem: Doc) => {
+ activeItem.presEaseFunc = activeItem.presEaseFunc === 'linear' ? 'ease' : 'linear';
+ this.selectedArray.forEach(doc => (doc.presEaseFunc = activeItem.presEaseFunc));
+ };
@undoBatch
@action
- updateEffectDirection = (effect: PresEffect, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presEffectDirection = effect));
+ updateEffectDirection = (effect: PresEffectDirection, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presEffectDirection = effect));
@undoBatch
@action
@@ -1198,13 +1228,36 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
_batch: UndoManager.Batch | undefined = undefined;
+ public static inputter = (min: string, step: string, max: string, value: number, active: boolean, change: (val: string) => void, hmargin?: number) => {
+ let batch: any;
+ return (
+ <input
+ type="range"
+ step={step}
+ min={min}
+ max={max}
+ value={value}
+ style={{ marginLeft: hmargin, marginRight: hmargin, width: `calc(100% - ${2 * (hmargin ?? 0)}px)` }}
+ className={`toolbar-slider ${active ? '' : 'none'}`}
+ onPointerDown={e => {
+ batch = UndoManager.StartBatch('pres slider');
+ e.stopPropagation();
+ }}
+ onPointerUp={() => batch?.end()}
+ onChange={e => {
+ e.stopPropagation();
+ change(e.target.value);
+ }}
+ />
+ );
+ };
@computed get transitionDropdown() {
const activeItem: Doc = this.activeItem;
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)}>
+ <div className={`presBox-dropdownOption ${activeItem.presEffect === effect || (effect === PresEffect.None && !activeItem.presEffect) ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateEffect(effect)}>
{effect}
</div>
);
@@ -1213,47 +1266,26 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
{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';
+ const presDirection = (direction: PresEffectDirection, icon: string, gridColumn: number, gridRow: number, opts: object) => {
+ const color = activeItem.presEffectDirection === direction || (direction === PresEffectDirection.Center && !activeItem.presEffectDirection) ? Colors.LIGHT_BLUE : 'black';
return (
- <Tooltip title={<div className="dash-tooltip">{diretion}</div>}>
+ <Tooltip title={<div className="dash-tooltip">{direction}</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)}>
+ style={{ ...opts, border: direction === PresEffectDirection.Center ? `solid 2px ${color}` : undefined, borderRadius: '100%', cursor: 'pointer', gridColumn, gridRow, justifySelf: 'center', color }}
+ onClick={() => this.updateEffectDirection(direction)}>
{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={e => {
- this._batch = UndoManager.StartBatch('pres slider');
- e.stopPropagation();
- }}
- 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;
const zoom = NumCast(activeItem.presZoom, 1) * 100;
- let duration = activeItem.presDuration ? NumCast(activeItem.presDuration) / 1000 : 2;
+ let duration = activeItem.presDuration ? NumCast(activeItem.presDuration) / 1000 : 0;
if (activeItem.type === DocumentType.AUDIO) duration = NumCast(activeItem.duration);
- const effect = this.activeItem.presEffect ? this.activeItem.presEffect : 'None';
- activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : 'Zoom';
+ const effect = activeItem.presEffect ? activeItem.presEffect : PresMovement.None;
+ // activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : PresMovement.Zoom;
return (
<div
className={`presBox-ribbon ${this._transitionTools && this.layoutDoc.presStatus === PresStatus.Edit ? 'active' : ''}`}
@@ -1277,6 +1309,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<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' }}>
{isPresCollection || (isPresCollection && isPinWithView) ? null : presMovement(PresMovement.None)}
+ {presMovement(PresMovement.Center)}
{presMovement(PresMovement.Zoom)}
{presMovement(PresMovement.Pan)}
{isPresCollection || (isPresCollection && isPinWithView) ? null : presMovement(PresMovement.Jump)}
@@ -1296,9 +1329,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>
</div>
</div>
- {inputter('0', '1', '100', 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>
+ {PresBox.inputter('0', '1', '100', zoom, activeItem.presMovement === PresMovement.Zoom, this.setZoom)}
+ <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
+ <div className="presBox-subheading">Transition Speed</div>
<div className="ribbon-property">
<input className="presBox-input" type="number" value={transitionSpeed} onKeyDown={e => e.stopPropagation()} onChange={action(e => this.setTransitionTime(e.target.value))} /> s
</div>
@@ -1311,8 +1344,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>
</div>
</div>
- {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'}`}>
+ {PresBox.inputter('0.1', '0.1', '100', transitionSpeed, true, this.setTransitionTime)}
+ <div className={'slider-headers'}>
<div className="slider-text">Fast</div>
<div className="slider-text">Medium</div>
<div className="slider-text">Slow</div>
@@ -1329,6 +1362,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</Tooltip>
)}
{isPresCollection ? null : (
+ <Tooltip title={<div className="dash-tooltip">{'Hide while presented'}</div>}>
+ <div className={`ribbon-toggle ${activeItem.presHide ? 'active' : ''}`} onClick={() => this.updateHide(activeItem)}>
+ Hide
+ </div>
+ </Tooltip>
+ )}
+ {isPresCollection ? null : (
<Tooltip title={<div className="dash-tooltip">{'Hide after presented'}</div>}>
<div className={`ribbon-toggle ${activeItem.presHideAfter ? 'active' : ''}`} onClick={() => this.updateHideAfter(activeItem)}>
Hide after
@@ -1340,6 +1380,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
Lightbox
</div>
</Tooltip>
+ <Tooltip title={<div className="dash-tooltip">{'Transition movement style'}</div>}>
+ <div className="ribbon-toggle" onClick={() => this.updateEaseFunc(activeItem)}>
+ {`${StrCast(activeItem.presEaseFunc, 'ease')}`}
+ </div>
+ </Tooltip>
</div>
{type === DocumentType.AUDIO || type === DocumentType.VID ? null : (
<>
@@ -1357,7 +1402,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>
</div>
</div>
- {inputter('0.1', '0.1', '20', duration, targetDoc.type !== DocumentType.AUDIO, this.setDurationTime)}
+ {PresBox.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>
@@ -1369,6 +1414,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
{isPresCollection ? null : (
<div className="ribbon-box">
Effects
+ <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
+ <div className="presBox-subheading">Play Audio Annotation</div>
+ <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.followLinkAudio = !BoolCast(activeItem.followLinkAudio))} checked={BoolCast(activeItem.followLinkAudio)} />
+ </div>
<div
className="presBox-dropdown"
onClick={action(e => {
@@ -1376,7 +1425,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
this._openEffectDropdown = !this._openEffectDropdown;
})}
style={{ borderBottomLeftRadius: this._openEffectDropdown ? 0 : 5, border: this._openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
- {effect.toString()}
+ {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()}>
{presEffect(PresEffect.None)}
@@ -1387,16 +1436,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
{presEffect(PresEffect.Roll)}
</div>
</div>
- <div className="ribbon-doubleButton" style={{ display: effect === 'None' ? 'none' : 'inline-flex' }}>
+ <div className="ribbon-doubleButton" style={{ display: effect === PresEffectDirection.None ? 'none' : 'inline-flex' }}>
<div className="presBox-subheading">Effect direction</div>
<div className="ribbon-property">{StrCast(this.activeItem.presEffectDirection)}</div>
</div>
- <div className="effectDirection" style={{ display: effect === 'None' ? 'none' : 'grid', width: 40 }}>
- {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 className="effectDirection" style={{ display: effect === PresEffectDirection.None ? 'none' : 'grid', width: 40 }}>
+ {presDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
+ {presDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
+ {presDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
+ {presDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
+ {presDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
</div>
</div>
)}
@@ -1415,7 +1464,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
applyTo = (array: Doc[]) => {
this.updateMovement(this.activeItem.presMovement as PresMovement, true);
this.updateEffect(this.activeItem.presEffect as PresEffect, true);
- this.updateEffectDirection(this.activeItem.presEffectDirection as PresEffect, true);
+ this.updateEffectDirection(this.activeItem.presEffectDirection as PresEffectDirection, true);
const { presTransition, presDuration, presHideBefore, presHideAfter } = this.activeItem;
array.forEach(curDoc => {
curDoc.presTransition = presTransition;
@@ -1745,10 +1794,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const presData = Cast(this.rootDoc.data, listSpec(Doc));
if (data && presData) {
data.push(doc);
- TabDocView.PinDoc(doc);
+ TabDocView.PinDoc(doc, {});
this.gotoDocument(this.childDocs.length, this.activeItem);
} else {
- this.props.addDocTab(doc, 'add:right');
+ this.props.addDocTab(doc, OpenWhere.addRight);
}
}
};
@@ -1781,7 +1830,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
className="dropdown-play-button"
onClick={undoBatch(
action(() => {
- this.updateMinimize();
+ this.enterMinimize();
this.turnOffEdit(true);
this.gotoDocument(this.itemIndex, this.activeItem);
})
@@ -1804,37 +1853,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
scrollFocus = () => {
- this.startOrPause(false);
+ // this.gotoDocument(0);
+ // this.startOrPause(false);
return undefined;
};
- // Case in which the document has keyframes to navigate to next key frame
- @action
- nextKeyframe = (tagDoc: Doc, curDoc: Doc): void => {
- const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]);
- const currentFrame = Cast(tagDoc._currentFrame, 'number', null);
- if (currentFrame === undefined) {
- tagDoc._currentFrame = 0;
- // CollectionFreeFormDocumentView.setupScroll(tagDoc, 0);
- // CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
- }
- // if (tagDoc.editScrollProgressivize) CollectionFreeFormDocumentView.updateScrollframe(tagDoc, currentFrame);
- CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, tagDoc);
- tagDoc._currentFrame = Math.max(0, (currentFrame || 0) + 1);
- tagDoc.lastFrame = Math.max(NumCast(tagDoc._currentFrame), NumCast(tagDoc.lastFrame));
- };
-
- @action
- prevKeyframe = (tagDoc: Doc, actItem: Doc): void => {
- const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]);
- const currentFrame = Cast(tagDoc._currentFrame, 'number', null);
- if (currentFrame === undefined) {
- tagDoc._currentFrame = 0;
- // CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
- }
- CollectionFreeFormDocumentView.gotoKeyframe(childDocs.slice());
- tagDoc._currentFrame = Math.max(0, (currentFrame || 0) - 1);
- };
+ _keyTimer: NodeJS.Timeout | undefined;
/**
* Returns the collection type as a string for headers
@@ -1860,123 +1884,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@observable private openActiveColorPicker: boolean = false;
@observable private openViewedColorPicker: boolean = false;
- @computed get progressivizeDropdown() {
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
- if (activeItem && targetDoc) {
- 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 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>
- {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-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editZoomProgressivize}>
- Edit
- </div>
- </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>
- </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>
- );
- }
- }
-
@undoBatch
@action
switchActive = (color: ColorState) => {
@@ -2011,262 +1918,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
@action
- turnOffEdit = (paths?: boolean) => {
- // Turn off paths
- if (paths) {
- const srcContext = Cast(this.rootDoc.presCollection, Doc, null);
- if (srcContext) this.togglePath(srcContext, true);
- }
- // Turn off the progressivize editors for each document
- this.childDocs.forEach(doc => {
- doc.editSnapZoomProgressivize = false;
- doc.editZoomProgressivize = false;
- const targetDoc = Cast(doc.presentationTargetDoc, Doc, null);
- if (targetDoc) {
- targetDoc.editZoomProgressivize = false;
- // targetDoc.editScrollProgressivize = false;
- }
- });
- };
-
- //Toggle whether the user edits or not
- @action
- editZoomProgressivize = (e: React.MouseEvent) => {
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
- if (!targetDoc.editZoomProgressivize) {
- if (!activeItem.zoomProgressivize) activeItem.zoomProgressivize = true;
- targetDoc.zoomProgressivize = true;
- targetDoc.editZoomProgressivize = true;
- activeItem.editZoomProgressivize = true;
- } else {
- targetDoc.editZoomProgressivize = false;
- activeItem.editZoomProgressivize = false;
- }
- };
-
- //Toggle whether the user edits or not
- @action
- editScrollProgressivize = (e: React.MouseEvent) => {
- const targetDoc: Doc = this.targetDoc;
- if (!targetDoc.editScrollProgressivize) {
- if (!targetDoc.scrollProgressivize) {
- targetDoc.scrollProgressivize = true;
- this.activeItem.scrollProgressivize = true;
- }
- targetDoc.editScrollProgressivize = true;
- } else {
- targetDoc.editScrollProgressivize = false;
- }
- };
-
- //Progressivize Zoom
- @action
- progressivizeScroll = (e: React.MouseEvent) => {
- e.stopPropagation();
- this.activeItem.scrollProgressivize = !this.activeItem.scrollProgressivize;
- const targetDoc: Doc = this.targetDoc;
- targetDoc.scrollProgressivize = !targetDoc.scrollProgressivize;
- // CollectionFreeFormDocumentView.setupScroll(targetDoc, NumCast(targetDoc._currentFrame));
- if (targetDoc.editScrollProgressivize) {
- targetDoc.editScrollProgressivize = false;
- targetDoc._currentFrame = 0;
- targetDoc.lastFrame = 0;
- }
- };
-
- //Progressivize Zoom
- @action
- progressivizeZoom = (e: React.MouseEvent) => {
- e.stopPropagation();
- const activeItem: Doc = this.activeItem;
- activeItem.zoomProgressivize = !activeItem.zoomProgressivize;
- const targetDoc: Doc = this.targetDoc;
- targetDoc.zoomProgressivize = !targetDoc.zoomProgressivize;
- CollectionFreeFormDocumentView.setupZoom(activeItem, targetDoc);
- if (activeItem.editZoomProgressivize) {
- activeItem.editZoomProgressivize = false;
- targetDoc._currentFrame = 0;
- targetDoc.lastFrame = 0;
- }
- };
-
- //Progressivize Child Docs
- @action
- editProgressivize = (e: React.MouseEvent) => {
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
- targetDoc._currentFrame = targetDoc.lastFrame;
- if (!targetDoc.editProgressivize) {
- if (!activeItem.presProgressivize) {
- activeItem.presProgressivize = true;
- targetDoc.presProgressivize = true;
- }
- targetDoc.editProgressivize = true;
- } else {
- targetDoc.editProgressivize = false;
- }
- };
-
- @action
- progressivizeChild = (e: React.MouseEvent) => {
- e.stopPropagation();
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
- const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
- if (!activeItem.presProgressivize) {
- targetDoc.keyFrameEditing = false;
- activeItem.presProgressivize = true;
- targetDoc.presProgressivize = true;
- targetDoc._currentFrame = 0;
- docs.forEach((doc, i) => CollectionFreeFormDocumentView.setupKeyframes([doc], i, true));
- targetDoc.lastFrame = targetDoc.lastFrame ? NumCast(targetDoc.lastFrame) : docs.length - 1;
- } else {
- // targetDoc.editProgressivize = false;
- activeItem.presProgressivize = false;
- targetDoc.presProgressivize = false;
- targetDoc._currentFrame = 0;
- targetDoc.keyFrameEditing = true;
- }
- };
-
- @action
- checkMovementLists = (doc: Doc, xlist: any, ylist: any) => {
- const x: List<number> = xlist;
- const y: List<number> = ylist;
- const tags: JSX.Element[] = [];
- let pathPoints = ''; //List of all of the pathpoints that need to be added
- for (let i = 0; i < x.length - 1; i++) {
- if (y[i] || x[i]) {
- if (i === 0) pathPoints = x[i] - 11 + ',' + (y[i] + 33);
- else pathPoints = pathPoints + ' ' + (x[i] - 11) + ',' + (y[i] + 33);
- tags.push(
- <div className="progressivizeMove-frame" style={{ position: 'absolute', top: y[i], left: x[i] }}>
- {i}
- </div>
- );
- }
- }
- tags.push(
- <svg style={{ overflow: 'visible', position: 'absolute' }}>
- <polyline
- points={pathPoints}
- style={{
- position: 'absolute',
- opacity: 1,
- stroke: '#000000',
- strokeWidth: 2,
- strokeDasharray: '10 5',
- }}
- fill="none"
- />
- </svg>
- );
- return tags;
- };
-
- @observable
- toggleDisplayMovement = (doc: Doc) => (doc.displayMovement = !doc.displayMovement);
-
- @action
- checkList = (doc: Doc, list: any): number => {
- const x: List<number> = list;
- 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)];
- }
- return 100;
- };
-
- @computed get progressivizeChildDocs() {
- const targetDoc: Doc = this.targetDoc;
- const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
- const tags: JSX.Element[] = [];
- docs.forEach((doc, index) => {
- if (doc['x-indexed'] && doc['y-indexed']) {
- tags.push(<div style={{ position: 'absolute', display: doc.displayMovement ? 'block' : 'none' }}>{this.checkMovementLists(doc, doc['x-indexed'], doc['y-indexed'])}</div>);
- }
- tags.push(
- <div
- className="progressivizeButton"
- key={index}
- onPointerLeave={() => {
- if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0;
- }}
- onPointerOver={() => {
- if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5;
- }}
- onClick={e => {
- this.toggleDisplayMovement(doc);
- e.stopPropagation();
- }}
- style={{ backgroundColor: doc.displayMovement ? Colors.LIGHT_BLUE : '#c8c8c8', top: NumCast(doc.y), left: NumCast(doc.x) }}>
- <div className="progressivizeButton-prev">
- <FontAwesomeIcon
- icon={'caret-left'}
- size={'lg'}
- onClick={e => {
- e.stopPropagation();
- this.prevAppearFrame(doc, index);
- }}
- />
- </div>
- <div className="progressivizeButton-frame">{NumCast(doc.appearFrame)}</div>
- <div className="progressivizeButton-next">
- <FontAwesomeIcon
- icon={'caret-right'}
- size={'lg'}
- onClick={e => {
- e.stopPropagation();
- this.nextAppearFrame(doc, index);
- }}
- />
- </div>
- </div>
- );
- });
- return tags;
- }
-
- @action
- 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) => {
- doc.appearFrame = Math.max(0, (Cast(doc.appearFrame, 'number', null) ?? 0) - 1);
- this.updateOpacityList(doc['opacity-indexed'], NumCast(doc.appearFrame));
- };
-
- @action
- updateOpacityList = (list: any, frame: number) => {
- const x: List<number> = list;
- if (x && x.length >= frame) {
- for (let i = 0; i < x.length; i++) {
- if (i < frame) {
- x[i] = 0;
- } else if (i >= frame) {
- x[i] = 1;
- }
- }
- list = x;
- } else {
- x.length = frame + 1;
- for (let i = 0; i < x.length; i++) {
- if (i < frame) {
- x[i] = 0;
- } else if (i >= frame) {
- x[i] = 1;
- }
- }
- list = x;
- }
- };
+ turnOffEdit = (paths?: boolean) => paths && this.togglePath(true); // Turn off paths
@computed
get toolbarWidth(): number {
@@ -2289,11 +1941,11 @@ 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 }}
+ style={{ opacity: this.childDocs.length > 1 ? 1 : 0.3, color: this._pathBoolean ? Colors.MEDIUM_BLUE : 'white', width: isMini ? '100%' : undefined }}
className={'toolbar-button'}
- onClick={this.childDocs.length > 1 && this.layoutDoc.presCollection ? this.viewPaths : undefined}>
+ onClick={this.childDocs.length > 1 ? () => this.togglePath() : undefined}>
<FontAwesomeIcon icon={'exchange-alt'} />
</div>
</Tooltip>
@@ -2332,8 +1984,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get topPanel() {
const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
const isMini: boolean = this.toolbarWidth <= 100;
+ const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc);
return (
- <div className="presBox-buttons" style={{ background: Doc.ActivePresentation === this.rootDoc ? Colors.LIGHT_BLUE : undefined, display: !this.rootDoc._chromeHidden ? 'none' : undefined }}>
+ <div className={`presBox-buttons${inOverlay ? ' inOverlay' : ''}`} style={{ background: Doc.ActivePresentation === this.rootDoc ? Colors.LIGHT_BLUE : undefined, 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={StopEvent} value={CollectionViewType.Stacking}>
@@ -2350,7 +2003,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</select>
)}
<div className="presBox-presentPanel" style={{ opacity: this.childDocs.length ? 1 : 0.3 }}>
- <span className={`presBox-button ${this.layoutDoc.presStatus === 'edit' ? 'present' : ''}`}>
+ <span className={`presBox-button ${this.layoutDoc.presStatus === PresStatus.Edit ? 'present' : ''}`}>
<div
className="presBox-button-left"
onClick={undoBatch(() => {
@@ -2379,89 +2032,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
);
}
- @action
- getList = (list: any): List<number> => list;
-
- @action
- updateList = (list: any): List<number> => {
- const targetDoc: Doc = this.targetDoc;
- const x: List<number> = list;
- x[x.length - 1] = NumCast(targetDoc._scrollY);
- return x;
- };
-
- @action
- newFrame = () => {
- const activeItem: Doc = this.activeItem;
- 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;
- }
- };
-
- @computed get frameListHeader() {
- return (
- <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>}>
- <div
- className={'headerButton'}
- onClick={e => {
- e.stopPropagation();
- this.newFrame();
- }}>
- <FontAwesomeIcon icon={'plus'} onPointerDown={e => e.stopPropagation()} />
- </div>
- </Tooltip>
- <Tooltip title={<div className="dash-tooltip">{'Edit in collection'}</div>}>
- <div
- className={'headerButton'}
- onClick={e => {
- e.stopPropagation();
- console.log('New frame');
- }}>
- <FontAwesomeIcon icon={'edit'} onPointerDown={e => e.stopPropagation()} />
- </div>
- </Tooltip>
- </div>
- </div>
- );
- }
-
- @computed get frameList() {
- 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 = 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>
- );
- }
-
@computed get playButtons() {
const presEnd: boolean = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1;
const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0;
+ const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc);
// 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>}>
- <div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : 'white' }} onClick={() => (this.layoutDoc.presLoop = !this.layoutDoc.presLoop)}>
+ <div
+ className="presPanel-button"
+ style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : 'white' }}
+ onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => (this.layoutDoc.presLoop = !this.layoutDoc.presLoop), false, false)}>
<FontAwesomeIcon icon={'redo-alt'} />
</div>
</Tooltip>
@@ -2469,43 +2051,77 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div
className="presPanel-button"
style={{ opacity: presStart ? 0.4 : 1 }}
- onClick={e => {
- this.back();
- if (this._presTimer) {
- clearTimeout(this._presTimer);
- this.layoutDoc.presStatus = PresStatus.Manual;
- }
- e.stopPropagation();
- }}>
+ onPointerDown={e =>
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ emptyFunction,
+ () => {
+ this.back();
+ if (this._presTimer) {
+ clearTimeout(this._presTimer);
+ this.layoutDoc.presStatus = PresStatus.Manual;
+ }
+ e.stopPropagation();
+ },
+ false,
+ false
+ )
+ }>
<FontAwesomeIcon icon={'arrow-left'} />
</div>
<Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}>
- <div className="presPanel-button" onClick={e => this.startOrPause(true)}>
+ <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.startOrPause(true), false, false)}>
<FontAwesomeIcon icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? 'pause' : 'play'} />
</div>
</Tooltip>
<div
className="presPanel-button"
style={{ opacity: presEnd ? 0.4 : 1 }}
- onClick={e => {
- this.next();
- if (this._presTimer) {
- clearTimeout(this._presTimer);
- this.layoutDoc.presStatus = PresStatus.Manual;
- }
- e.stopPropagation();
- }}>
+ onPointerDown={e =>
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ emptyFunction,
+ () => {
+ this.next();
+ if (this._presTimer) {
+ clearTimeout(this._presTimer);
+ this.layoutDoc.presStatus = PresStatus.Manual;
+ }
+ e.stopPropagation();
+ },
+ false,
+ false
+ )
+ }>
<FontAwesomeIcon icon={'arrow-right'} />
</div>
<div className="presPanel-divider"></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.nextSlide(0)}>
+ <div
+ className="presPanel-button"
+ style={{ border: 'solid 1px white' }}
+ onPointerDown={e =>
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ emptyFunction,
+ () => {
+ this.nextSlide(0);
+ },
+ false,
+ false
+ )
+ }>
<b>1</b>
</div>
</Tooltip>
- <div className="presPanel-button-text" onClick={() => this.gotoDocument(0, this.activeItem)} style={{ display: this.props.PanelWidth() > 250 ? 'inline-flex' : 'none' }}>
- Slide {this.itemIndex + 1} / {this.childDocs.length}
- {this.playButtonFrames}
+ <div className="presPanel-button-text" onClick={() => this.gotoDocument(0, this.activeItem)} style={{ display: inOverlay || this.props.PanelWidth() > 250 ? 'inline-flex' : 'none' }}>
+ {`${inOverlay ? '' : 'Slide'} ${this.itemIndex + 1} / ${this.childDocs.length}`}
</div>
<div className="presPanel-divider"></div>
{this.props.PanelWidth() > 250 ? (
@@ -2513,14 +2129,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
className="presPanel-button-text"
onClick={undoBatch(
action(() => {
- this.layoutDoc.presStatus = 'edit';
+ this.layoutDoc.presStatus = PresStatus.Edit;
clearTimeout(this._presTimer);
})
)}>
EXIT
</div>
) : (
- <div className="presPanel-button" onClick={undoBatch(action(() => (this.layoutDoc.presStatus = 'edit')))}>
+ <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, this.exitClicked, false, false)}>
<FontAwesomeIcon icon={'times'} />
</div>
)}
@@ -2531,7 +2147,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@action
startOrPause = (makeActive = true) => {
makeActive && this.updateCurrentPresentation();
- if (this.layoutDoc.presStatus === PresStatus.Manual || this.layoutDoc.presStatus === PresStatus.Edit) this.startAutoPres(this.itemIndex);
+ if (this.layoutDoc.presStatus === PresStatus.Manual || this.layoutDoc.presStatus === PresStatus.Edit) this.startPresentation(this.itemIndex);
else this.pauseAutoPres();
};
@@ -2554,15 +2170,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
@undoBatch
@action
- exitClicked = (e: PointerEvent) => {
- this.updateMinimize();
- this.layoutDoc.presStatus = PresStatus.Edit;
+ exitClicked = () => {
+ this.layoutDoc.presStatus = this._exitTrail?.() ?? this.exitMinimize();
clearTimeout(this._presTimer);
};
- @action
- startMarqueeCreateSlide = () => (PresBox.startMarquee = true);
-
AddToMap = (treeViewDoc: Doc, index: number[]): Doc[] => {
var indexNum = 0;
for (let i = 0; i < index.length; i++) {
@@ -2594,8 +2206,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
// needed to ensure that the childDocs are loaded for looking up fields
this.childDocs.slice();
const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
- const presEnd: boolean = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1;
- const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0;
+ const presEnd = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1;
+ const presStart = !this.layoutDoc.presLoop && this.itemIndex === 0;
+ const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc);
return this.props.addDocTab === returnFalse ? ( // bcz: hack!! - addDocTab === returnFalse only when this is being rendered by the OverlayView which means the doc is a mini player
<div className="miniPres" onClick={e => e.stopPropagation()} onPointerEnter={action(e => (this._forceKeyEvents = true))}>
<div
@@ -2629,7 +2242,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</Tooltip>
<div className="presPanel-button-text">
Slide {this.itemIndex + 1} / {this.childDocs.length}
- {this.playButtonFrames}
</div>
<div className="presPanel-divider" />
<div className="presPanel-button-text" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.exitClicked, false, false)}>
@@ -2638,7 +2250,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>
</div>
) : (
- <div className="presBox-cont" style={{ minWidth: DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc) ? 240 : undefined }}>
+ <div className="presBox-cont" style={{ minWidth: inOverlay ? PresBox.minimizedWidth : undefined }}>
{this.topPanel}
{this.toolbar}
{this.newDocumentToolbarDropdown}
@@ -2652,6 +2264,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
PanelHeight={this.panelHeight}
childIgnoreNativeSize={true}
moveDocument={returnFalse}
+ ignoreUnrendered={true}
//childFitWidth={returnTrue}
childOpacity={returnOne}
childLayoutTemplate={this.childLayoutTemplate}
@@ -2664,7 +2277,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
ScreenToLocalTransform={this.getTransform}
AddToMap={this.AddToMap}
RemFromMap={this.RemFromMap}
- hierarchyIndex={[]}
+ hierarchyIndex={emptyPath}
/>
) : null}
</div>
@@ -2684,7 +2297,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
static NavigateToDoc(bestTarget: Doc, activeItem: Doc) {
const srcContext = Cast(bestTarget.context, Doc, null) ?? Cast(Cast(bestTarget.annotationOn, Doc, null)?.context, Doc, null);
const openInTab = (doc: Doc, finished?: () => void) => {
- CollectionDockingView.AddSplit(doc, 'right');
+ CollectionDockingView.AddSplit(doc, OpenWhereMod.right);
finished?.();
};
PresBox.NavigateToTarget(bestTarget, activeItem, openInTab, srcContext);
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 5d14a0e9a..f1c97d26a 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -50,9 +50,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get collapsedHeight() {
return 35;
} // the collapsed height changes depending on the state of the presBox. We could store this on the presentation element template if it's used by only one presentation - but if it's shared by multiple, then this value must be looked up
- @computed get presStatus() {
- return this.presBox.presStatus;
- }
@computed get selectedArray() {
return this.presBoxView?.selectedArray;
}
@@ -364,14 +361,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
@computed get recordingIsInOverlay() {
- let isInOverlay = false;
- DocListCast(Doc.MyOverlayDocs.data).forEach(doc => {
- if (doc.slides === this.rootDoc) {
- isInOverlay = true;
- // return
- }
- });
- return isInOverlay;
+ return DocListCast(Doc.MyOverlayDocs.data).some(doc => doc.slides === this.rootDoc);
}
// a previously recorded video will have timecode defined
@@ -502,16 +492,17 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
</Tooltip>
);
}
- if (this.indexInPres === 0) {
+ if (this.indexInPres !== 0) {
items.push(
<Tooltip key="arrow" title={<div className="dash-tooltip">{activeItem.groupWithUp ? 'Ungroup' : 'Group with up'}</div>}>
<div
className="slideButton"
- onClick={() => (activeItem.groupWithUp = !activeItem.groupWithUp)}
+ onClick={() => (activeItem.groupWithUp = (NumCast(activeItem.groupWithUp) + 1) % 3)}
style={{
zIndex: 1000 - this.indexInPres,
fontWeight: 700,
backgroundColor: activeItem.groupWithUp ? (presColorBool ? presBoxColor : Colors.MEDIUM_BLUE) : undefined,
+ outline: NumCast(activeItem.groupWithUp) > 1 ? 'solid black 1px' : undefined,
height: activeItem.groupWithUp ? 53 : 18,
transform: activeItem.groupWithUp ? 'translate(0, -17px)' : undefined,
}}>
diff --git a/src/client/views/nodes/trails/PresEnums.ts b/src/client/views/nodes/trails/PresEnums.ts
index c6a222c3a..564829d54 100644
--- a/src/client/views/nodes/trails/PresEnums.ts
+++ b/src/client/views/nodes/trails/PresEnums.ts
@@ -1,6 +1,7 @@
export enum PresMovement {
Zoom = 'zoom',
Pan = 'pan',
+ Center = 'center',
Jump = 'jump',
None = 'none',
}
@@ -14,11 +15,15 @@ export enum PresEffect {
Bounce = 'Bounce',
Roll = 'Roll',
None = 'None',
+}
+
+export enum PresEffectDirection {
Left = 'Enter from left',
Right = 'Enter from right',
Center = 'Enter from center',
Top = 'Enter from Top',
Bottom = 'Enter from bottom',
+ None = 'None',
}
export enum PresStatus {