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.tsx47
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx58
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx19
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx57
-rw-r--r--src/client/views/nodes/DocumentView.tsx143
-rw-r--r--src/client/views/nodes/FilterBox.tsx2
-rw-r--r--src/client/views/nodes/FunctionPlotBox.tsx24
-rw-r--r--src/client/views/nodes/ImageBox.tsx107
-rw-r--r--src/client/views/nodes/LabelBox.tsx2
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx1
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx2
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx153
-rw-r--r--src/client/views/nodes/PDFBox.tsx51
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx2
-rw-r--r--src/client/views/nodes/ScriptingBox.tsx5
-rw-r--r--src/client/views/nodes/VideoBox.tsx89
-rw-r--r--src/client/views/nodes/WebBox.tsx124
-rw-r--r--src/client/views/nodes/button/FontIconBox.tsx47
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx5
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx22
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx66
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx21
-rw-r--r--src/client/views/nodes/trails/PresBox.scss2
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx396
-rw-r--r--src/client/views/nodes/trails/PresElementBox.scss6
-rw-r--r--src/client/views/nodes/trails/PresElementBox.tsx141
26 files changed, 872 insertions, 720 deletions
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 1d59d3356..4c26468b9 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -18,6 +18,7 @@ import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
import './AudioBox.scss';
import { FieldView, FieldViewProps } from './FieldView';
+import { PinProps, PresBox } from './trails';
/**
* AudioBox
@@ -37,7 +38,7 @@ declare class MediaRecorder {
constructor(e: any); // whatever MediaRecorder has
}
-enum media_state {
+export enum media_state {
PendingRecording = 'pendingRecording',
Recording = 'recording',
Paused = 'paused',
@@ -70,12 +71,6 @@ 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();
}
@@ -117,6 +112,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@action
componentDidMount() {
+ this.props.setContentView?.(this);
if (this.path) {
this.mediaState = media_state.Paused;
this.setPlayheadTime(NumCast(this.layoutDoc.clipStart));
@@ -136,20 +132,19 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
return { la1, la2, linkTime };
}
- getAnchor = (addAsAnnotation: boolean) => {
- return (
+ getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
+ const anchor =
CollectionStackedTimeline.createAnchor(
this.rootDoc,
this.dataDoc,
this.annotationKey,
- '_timecodeToShow' /* audioStart */,
- '_timecodeToHide' /* audioEnd */,
this._ele?.currentTime || Cast(this.props.Document._currentTimecode, 'number', null) || (this.mediaState === media_state.Recording ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined),
undefined,
undefined,
addAsAnnotation
- ) || this.rootDoc
- );
+ ) || this.rootDoc;
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.rootDoc);
+ return anchor;
};
// updates timecode and shows it in timeline, follows links at time
@@ -201,8 +196,9 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// removes from currently playing display
@action
removeCurrentlyPlaying = () => {
- if (CollectionStackedTimeline.CurrentlyPlaying) {
- const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc);
+ const docView = this.props.DocumentView?.();
+ if (CollectionStackedTimeline.CurrentlyPlaying && docView) {
+ const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView);
index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
}
};
@@ -210,11 +206,12 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// adds doc to currently playing display
@action
addCurrentlyPlaying = () => {
+ const docView = this.props.DocumentView?.();
if (!CollectionStackedTimeline.CurrentlyPlaying) {
CollectionStackedTimeline.CurrentlyPlaying = [];
}
- if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc) === -1) {
- CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc);
+ if (docView && CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView) === -1) {
+ CollectionStackedTimeline.CurrentlyPlaying.push(docView);
}
};
@@ -329,18 +326,28 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
};
- // pause play back
+ IsPlaying = () => this.mediaState === media_state.Playing;
+ TogglePause = () => {
+ if (this.mediaState === media_state.Paused) this.Play();
+ else this.pause();
+ };
+ // pause playback without removing from the playback list to allow user to play it again.
@action
- Pause = () => {
+ pause = () => {
if (this._ele) {
this._ele.pause();
this.mediaState = media_state.Paused;
// if paused in the middle of playback, prevents restart on next play
if (!this._finished) clearTimeout(this._play);
- this.removeCurrentlyPlaying();
}
};
+ // pause playback and remove from playback list
+ @action
+ Pause = () => {
+ this.pause();
+ this.removeCurrentlyPlaying();
+ };
// for dictation button, creates a text document for dictation
onFile = (e: any) => {
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index f8ef87fb1..901415d1e 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,7 +1,6 @@
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, Opt } from '../../../fields/Doc';
-import { InkField } from '../../../fields/InkField';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
import { ComputedField } from '../../../fields/ScriptField';
@@ -20,11 +19,10 @@ import { DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
import React = require('react');
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
- dataProvider?: (doc: Doc, replica: string) => { x: number; y: number; zIndex?: number; color?: string; backgroundColor?: string; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined;
+ dataProvider?: (doc: Doc, replica: string) => { x: number; y: number; zIndex?: number; rotation?: number; color?: string; backgroundColor?: string; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined;
sizeProvider?: (doc: Doc, replica: string) => { width: number; height: number } | undefined;
renderCutoffProvider: (doc: Doc) => boolean;
zIndex?: number;
- rotation: number;
dataTransition?: string;
replica: string;
CollectionFreeFormView: CollectionFreeFormView;
@@ -40,11 +38,13 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
{ key: '_rotation', val: 0 },
{ key: '_scrollTop' },
{ key: 'opacity', val: 1 },
+ { key: '_currentFrame' },
+ { key: 'viewScale', val: 1 },
{ key: 'viewScale', val: 1 },
{ key: 'panX' },
{ key: 'panY' },
]; // fields that are configured to be animatable using animation frames
- public static animStringFields = ['backgroundColor', 'color']; // fields that are configured to be animatable using animation frames
+ public static animStringFields = ['backgroundColor', 'color', 'fillColor']; // fields that are configured to be animatable using animation frames
public static animDataFields = (doc: Doc) => (Doc.LayoutFieldKey(doc) ? [Doc.LayoutFieldKey(doc)] : []); // fields that are configured to be animatable using animation frames
@observable _animPos: number[] | undefined = undefined;
@observable _contentView: DocumentView | undefined | null;
@@ -54,7 +54,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
}
get transform() {
- return `translate(${this.X}px, ${this.Y}px) rotate(${NumCast(this.Document.rotation, this.props.rotation)}deg)`;
+ return `translate(${this.X}px, ${this.Y}px) rotate(${NumCast(this.Rot, this.Rot)}deg)`;
}
get X() {
return this.dataProvider?.x ?? NumCast(this.Document.x);
@@ -65,6 +65,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
get ZInd() {
return this.dataProvider?.zIndex ?? NumCast(this.Document.zIndex);
}
+ get Rot() {
+ return this.dataProvider?.rotation ?? NumCast(this.Document._rotation);
+ }
get Opacity() {
return this.dataProvider?.opacity;
}
@@ -82,13 +85,14 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
}
styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string) => {
- if (doc === this.layoutDoc)
+ if (doc === this.layoutDoc) {
// prettier-ignore
switch (property) {
case StyleProp.Opacity: return this.Opacity; // only change the opacity for this specific document, not its children
case StyleProp.BackgroundColor: return this.BackgroundColor;
case StyleProp.Color: return this.Color;
}
+ }
return this.props.styleProvider?.(doc, props, property);
};
@@ -106,39 +110,22 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
}, {} as { [val: string]: Opt<string> });
}
- public static setValues(time: number, d: Doc, vals: { [val: string]: Opt<number> }) {
+ public static setStringValues(time: number, d: Doc, vals: { [val: string]: Opt<string> }) {
const timecode = Math.round(time);
Object.keys(vals).forEach(val => {
- const findexed = Cast(d[`${val}-indexed`], listSpec('number'), []).slice();
- findexed[timecode] = vals[val] as any as number;
- d[`${val}-indexed`] = new List<number>(findexed);
+ const findexed = Cast(d[`${val}-indexed`], listSpec('string'), []).slice();
+ findexed[timecode] = vals[val] as any as string;
+ d[`${val}-indexed`] = new List<string>(findexed);
});
}
- 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);
+ public static setValues(time: number, d: Doc, vals: { [val: string]: Opt<number> }) {
const timecode = Math.round(time);
- 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);
- });
+ Object.keys(vals).forEach(val => {
+ const findexed = Cast(d[`${val}-indexed`], listSpec('number'), []).slice();
+ findexed[timecode] = vals[val] as any as number;
+ d[`${val}-indexed`] = new List<number>(findexed);
});
- return newTimer;
- }
-
- 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) {
@@ -165,9 +152,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
}
CollectionFreeFormDocumentView.animFields.forEach(val => (doc[val.key] = ComputedField.MakeInterpolatedNumber(val.key, 'activeFrame', doc, currTimecode, val.val)));
CollectionFreeFormDocumentView.animStringFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedString(val, 'activeFrame', doc, currTimecode)));
- CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => (Doc.GetProto(doc)[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', Doc.GetProto(doc), currTimecode)));
- const targetDoc = doc.type === DocumentType.RTF ? Doc.GetProto(doc) : doc; // data fields, like rtf 'text' exist on the data doc, so
- doc !== targetDoc && (targetDoc.context = doc.context); // the computed fields don't see the layout doc -- need to copy the context to the data doc (HACK!!!) and set the activeFrame on the data doc (HACK!!!)
+ CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => (doc[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', doc, currTimecode)));
+ const targetDoc = doc; // data fields, like rtf 'text' exist on the data doc, so
+ //doc !== targetDoc && (targetDoc.context = doc.context); // the computed fields don't see the layout doc -- need to copy the context to the data doc (HACK!!!) and set the activeFrame on the data doc (HACK!!!)
targetDoc.activeFrame = ComputedField.MakeFunction('self.context?._currentFrame||0');
targetDoc.dataTransition = 'inherit';
});
@@ -203,7 +190,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
panelWidth = () => this.sizeProvider?.width || this.props.PanelWidth?.();
panelHeight = () => this.sizeProvider?.height || this.props.PanelHeight?.();
screenToLocalTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.X, -this.Y);
- focusDoc = (doc: Doc) => this.props.focus(doc, {});
returnThis = () => this;
render() {
TraceMobx();
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index dd03b9b99..ecffe6c4f 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -4,6 +4,7 @@ import { observer } from 'mobx-react';
import { Doc, Opt } from '../../../fields/Doc';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { emptyFunction, OmitKeys, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils';
+import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { SnappingManager } from '../../util/SnappingManager';
import { undoBatch } from '../../util/UndoManager';
@@ -12,6 +13,7 @@ import { StyleProp } from '../StyleProvider';
import './ComparisonBox.scss';
import { DocumentView, DocumentViewProps } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
+import { PinProps, PresBox } from './trails';
import React = require('react');
@observer
@@ -24,6 +26,10 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
@observable _animating = '';
+ componentDidMount() {
+ this.props.setContentView?.(this);
+ }
+
protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string, disposerId: number) => {
this._disposers[disposerId]?.();
if (ele) {
@@ -72,6 +78,17 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
return false;
};
+ getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
+ const anchor = Docs.Create.ImageanchorDocument({ title: 'ImgAnchor:' + this.rootDoc.title, presTransition: 1000, unrendered: true, annotationOn: this.rootDoc });
+ if (anchor) {
+ if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
+ /* addAsAnnotation &&*/ this.addDocument(anchor);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), clippable: true } }, this.rootDoc);
+ return anchor;
+ }
+ return this.rootDoc;
+ };
+
@undoBatch
clearDoc = (e: React.MouseEvent, fieldKey: string) => {
e.stopPropagation; // prevent click event action (slider movement) in registerSliding
@@ -103,7 +120,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
<>
<DocumentView
ref={r => {
- whichDoc !== targetDoc && r?.focus(whichDoc, { instant: true });
+ //whichDoc !== targetDoc && r?.focus(whichDoc, { instant: true });
}}
{...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit}
isContentActive={returnFalse}
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index a39e0f65f..a40599d85 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -16,6 +16,7 @@ import { LinkDescriptionPopup } from './LinkDescriptionPopup';
import { TaskCompletionBox } from './TaskCompletedBox';
import React = require('react');
import _ = require('lodash');
+import { PinProps } from './trails';
const higflyout = require('@hig/flyout');
export const { anchorPoints } = higflyout;
@@ -127,55 +128,15 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
returnFalse,
emptyFunction,
undoBatch(
- action((e, doubleTap) => {
- if (doubleTap && !this.props.StartLink) {
- if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
- DocumentLinksButton.StartLink = undefined;
- DocumentLinksButton.StartLinkView = undefined;
- DocumentLinksButton.AnnotationId = undefined;
- } else if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document) {
- const sourceDoc = DocumentLinksButton.StartLink;
- const targetDoc = this.props.View.ComponentView?.getAnchor?.(true) || this.props.View.Document;
- const linkDoc = DocUtils.MakeLink({ doc: sourceDoc }, { doc: targetDoc }, 'links'); //why is long drag here when this is used for completing links by clicking?
-
- LinkManager.currentLink = linkDoc;
-
- runInAction(() => {
- if (linkDoc) {
- TaskCompletionBox.textDisplayed = 'Link Created';
- TaskCompletionBox.popupX = e.screenX;
- TaskCompletionBox.popupY = e.screenY - 133;
- TaskCompletionBox.taskCompleted = true;
-
- LinkDescriptionPopup.popupX = e.screenX;
- LinkDescriptionPopup.popupY = e.screenY - 100;
- LinkDescriptionPopup.descriptionPopup = true;
-
- const rect = document.body.getBoundingClientRect();
- if (LinkDescriptionPopup.popupX + 200 > rect.width) {
- LinkDescriptionPopup.popupX -= 190;
- TaskCompletionBox.popupX -= 40;
- }
- if (LinkDescriptionPopup.popupY + 100 > rect.height) {
- LinkDescriptionPopup.popupY -= 40;
- TaskCompletionBox.popupY -= 40;
- }
-
- setTimeout(
- action(() => (TaskCompletionBox.taskCompleted = false)),
- 2500
- );
- }
- });
- }
- }
+ action(e => {
+ DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View);
})
)
);
};
public static finishLinkClick = undoBatch(
- action((screenX: number, screenY: number, startLink: Doc, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView) => {
+ action((screenX: number, screenY: number, startLink: Doc, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView, pinProps?: PinProps) => {
if (startLink === endLink) {
DocumentLinksButton.StartLink = undefined;
DocumentLinksButton.StartLinkView = undefined;
@@ -183,7 +144,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
DocumentLinksButton.AnnotationUri = undefined;
//!this.props.StartLink
} else if (startLink !== endLink) {
- endLink = endLinkView?.docView?._componentView?.getAnchor?.(true) || endLink;
+ endLink = endLinkView?.docView?._componentView?.getAnchor?.(true, pinProps) || endLink;
startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.(true) || startLink;
const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? 'hypothes.is annotation' : undefined, undefined, undefined, true);
@@ -279,11 +240,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
</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}
- onPointerDown={DocumentLinksButton.StartLink && this.completeLink}
- onClick={e => DocumentLinksButton.StartLink && DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View)}>
+ <div className={'documentLinksButton-endLink'} ref={this._linkButton} onPointerDown={DocumentLinksButton.StartLink && this.completeLink}>
<FontAwesomeIcon className="documentdecorations-icon" icon="link" />
</div>
) : null}
@@ -305,7 +262,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
top: 0,
pointerEvents: 'none',
}}>
- <Tooltip title={<div className="dash-tooltip">{title}</div>}>{this.linkButtonInner}</Tooltip>
+ {DocumentLinksButton.LinkEditorDocView ? this.linkButtonInner : <Tooltip title={<div className="dash-tooltip">{title}</div>}>{this.linkButtonInner}</Tooltip>}
</div>
);
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b94db2c6b..dcccd1143 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -3,21 +3,19 @@ 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 { 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';
import { List } from '../../../fields/List';
-import { ObjectField } from '../../../fields/ObjectField';
import { listSpec } from '../../../fields/Schema';
import { ScriptField } from '../../../fields/ScriptField';
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';
-import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, OmitKeys, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils';
+import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, OmitKeys, returnEmptyString, returnFalse, returnNone, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils';
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { DocServer } from '../../DocServer';
import { Docs, DocUtils } from '../../documents/Documents';
@@ -41,10 +39,11 @@ import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { DocComponent } from '../DocComponent';
import { EditableView } from '../EditableView';
+import { GestureOverlay } from '../GestureOverlay';
import { LightboxView } from '../LightboxView';
import { StyleProp } from '../StyleProvider';
import { CollectionFreeFormDocumentView } from './CollectionFreeFormDocumentView';
-import { DocumentContentsView } from './DocumentContentsView';
+import { DocumentContentsView, ObserverJsxParser } from './DocumentContentsView';
import { DocumentLinksButton } from './DocumentLinksButton';
import './DocumentView.scss';
import { FieldViewProps } from './FieldView';
@@ -54,7 +53,7 @@ import { LinkDocPreview } from './LinkDocPreview';
import { RadialMenu } from './RadialMenu';
import { ScriptingBox } from './ScriptingBox';
import { PresEffect, PresEffectDirection } from './trails';
-import { PinProps } from './trails/PresBox';
+import { PinProps, PresBox } from './trails/PresBox';
import React = require('react');
const { Howl } = require('howler');
@@ -67,13 +66,7 @@ declare class MediaRecorder {
constructor(e: any);
}
-export enum ViewAdjustment {
- resetView = 1,
- doNothing = 0,
-}
-
export enum OpenWhere {
- inPlace = 'inPlace',
lightbox = 'lightbox',
add = 'add',
addLeft = 'add:left',
@@ -97,34 +90,32 @@ export enum OpenWhereMod {
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
willPan?: boolean; // determines whether to pan to target document
- willPanZoom?: boolean; // determines whether to zoom in on target document
+ willZoomCentered?: 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)
+ preview?: boolean; // whether changes should be previewed by the componentView or written to the document
effect?: Doc; // animation effect for focus
noSelect?: boolean; // whether target should be selected after focusing
playAudio?: boolean; // whether to play audio annotation on focus
+ openLocation?: string; // where to open a missing document
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;
+export type DocFocusFunc = (doc: Doc, options: DocFocusOptions) => Opt<number>;
export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any;
export interface DocComponentView {
updateIcon?: () => void; // updates the icon representation of the document
- getAnchor?: (addAsAnnotation: boolean) => 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, options: DocFocusOptions) => Opt<number>; // returns the duration of the focus
+ getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => 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)
+ scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, 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
+ getView?: (doc: Doc) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined
+ addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox
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.
shrinkWrap?: () => void; // requests a document to display all of its contents with no white space. currently only implemented (needed?) for freeform views
menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected.
@@ -132,14 +123,15 @@ export interface DocComponentView {
getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
playFrom?: (time: number, endTime?: number) => void;
- Pause?: () => void;
- setFocus?: () => void;
+ Pause?: () => void; // pause a media document (eg, audio/video)
+ IsPlaying?: () => boolean; // is a media document playing
+ TogglePause?: (keep?: boolean) => void; // toggle media document playing state
+ setFocus?: () => void; // sets input focus to the componentView
componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null;
incrementalRendering?: () => void;
fieldKey?: string;
annotationKey?: string;
getTitle?: () => string;
- getScrollHeight?: () => number;
getCenter?: (xf: Transform) => { X: number; Y: number };
ptToScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
ptFromScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
@@ -170,7 +162,7 @@ export interface DocumentViewSharedProps {
dataTransition?: string; // specifies animation transition - used by collectionPile and potentially other layout engines when changing the size of documents so that the change won't be abrupt
styleProvider: Opt<StyleProviderFunc>;
focus: DocFocusFunc;
- fitWidth?: (doc: Doc) => boolean;
+ fitWidth?: (doc: Doc) => boolean | undefined;
docFilters: () => string[];
docRangeFilters: () => string[];
searchFilterDocs: () => Doc[];
@@ -252,6 +244,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
private _disposers: { [name: string]: IReactionDisposer } = {};
private _downX: number = 0;
private _downY: number = 0;
+ private _downTime: number = 0;
private _firstX: number = -1;
private _firstY: number = -1;
private _lastTap: number = 0;
@@ -582,35 +575,22 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
};
- focus = (anchor: Doc, options: DocFocusOptions) => {
- LightboxView.SetCookie(StrCast(anchor['cookies-set']));
- // 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 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: 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>(async res =>
- setTimeout(
- async () => res(endFocus ? await endFocus(didFocus || focusSpeed !== undefined) : ViewAdjustment.doNothing), //
- didFocus ? Math.max(0, (options.zoomTime ?? 500) - (Date.now() - startTime)) : 0
- )
- ),
- });
+ defaultRestoreTargetView = (docView: DocumentView, anchor: Doc, focusSpeed: number, options: DocFocusOptions) => {
+ const targetMatch =
+ Doc.AreProtosEqual(anchor, this.rootDoc) || // anchor is this document, so anchor's properties apply to this document
+ (DocCast(anchor)?.unrendered && Doc.AreProtosEqual(DocCast(anchor.annotationOn), this.rootDoc)) // the anchor is an unrendered annotation on this document, so anchor properties applie to this document
+ ? true
+ : false;
+ return targetMatch && PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined;
};
+
onClick = action((e: React.MouseEvent | React.PointerEvent) => {
if (!this.Document.ignoreClick && this.props.renderDepth >= 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
let stopPropagate = true;
let preventDefault = true;
const isScriptBox = () => StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name);
(this.rootDoc._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged() : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
- if (this._doubleTap && (this.props.Document.type !== DocumentType.FONTICON || this.onDoubleClickHandler)) {
+ if (this._doubleTap && (![DocumentType.FONTICON, DocumentType.PRES].includes(this.props.Document.type as any) || this.onDoubleClickHandler)) {
// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
if (this._timeout) {
clearTimeout(this._timeout);
@@ -637,8 +617,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
console.log
);
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, OpenWhere.lightbox, this.props.LayoutTemplate?.(), this.props.addDocTab), 'double tap');
+ } else if (!Doc.IsSystem(this.rootDoc) && (![DocumentType.INK].includes(this.rootDoc.type as any) || Doc.UserDoc().openInkInLightbox) && !this.rootDoc.isLinkButton) {
+ //UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, OpenWhere.lightbox, this.props.LayoutTemplate?.()), 'double tap');
+ UndoManager.RunInBatch(() => this.props.addDocTab(this.rootDoc, OpenWhere.lightbox), 'double tap');
SelectionManager.DeselectAll();
Doc.UnBrushDoc(this.props.Document);
}
@@ -664,12 +645,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
? this.props.select(false)
: '';
const clickFunc = () => (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'on click'));
- if (this.onDoubleClickHandler) {
+ if (this.onDoubleClickHandler && !this.props.Document.allowClickBeforeDoubleClick) {
runInAction(() => (this._pendingDoubleClick = true));
this._timeout = setTimeout(() => {
this._timeout = undefined;
clickFunc();
- }, 350);
+ }, 150);
} else clickFunc();
} else if (!this._longPress && this.allLinks.length && this.Document.type !== DocumentType.LINK && !isScriptBox() && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
SelectionManager.DeselectAll();
@@ -698,6 +679,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@action
onPointerDown = (e: React.PointerEvent): void => {
+ if (!(e.nativeEvent as any).DownDocView) (e.nativeEvent as any).DownDocView = GestureOverlay.DownDocView = this.props.DocumentView();
if (this.rootDoc.type === DocumentType.INK && Doc.ActiveTool === InkTool.Eraser) return;
// continue if the event hasn't been canceled AND we are using a mouse or this has an onClick or onDragStart function (meaning it is a button document)
if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool))) {
@@ -717,6 +699,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
);
this._downX = e.clientX;
this._downY = e.clientY;
+ this._downTime = Date.now();
if ((Doc.ActiveTool === InkTool.None || this.props.addDocTab === returnFalse) && !(this.props.Document.rootDocument && !(e.ctrlKey || e.button > 0))) {
// if this is part of a template, let the event go up to the tempalte root unless right/ctrl clicking
if (
@@ -776,10 +759,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
this._cursorTimer && clearTimeout(this._cursorTimer);
this._cursorPress = false;
+ const now = Date.now();
if (this.onPointerUpHandler?.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
this.onPointerUpHandler.script.run({ self: this.rootDoc, this: this.layoutDoc }, console.log);
- } else {
- this._doubleTap = Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2;
+ } else if (now - this._downTime < 300) {
+ this._doubleTap = now - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2;
// bcz: this is a placeholder. documents, when selected, should stopPropagation on doubleClicks if they want to keep the DocumentView from getting them
if (!this.props.isSelected(true) || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this.rootDoc.type) as any)) this._lastTap = Date.now(); // don't want to process the start of a double tap if the doucment is selected
}
@@ -787,7 +771,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@undoBatch
@action
- toggleFollowLink = (location: Opt<string>, zoom?: boolean, setTargetToggle?: boolean): void => {
+ toggleFollowLink = (zoom?: boolean, setTargetToggle?: boolean): void => {
this.Document.ignoreClick = false;
if (setTargetToggle) {
this.Document.followLinkToggle = !this.Document.followLinkToggle;
@@ -797,7 +781,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
if (this.Document._isLinkButton && !this.onClickHandler) {
zoom !== undefined && (this.Document.followLinkZoom = zoom);
- this.Document.followLinkLocation = location;
} else if (this.Document._isLinkButton && this.onClickHandler) {
this.Document._isLinkButton = false;
this.dataDoc.onClick = this.Document.onClick = this.layoutDoc.onClick = undefined;
@@ -866,13 +849,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@undoBatch
@action
- makeIntoPortal = async () => {
+ makeIntoPortal = () => {
const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document);
if (!portalLink) {
- 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');
+ DocUtils.MakeLink(
+ { doc: this.props.Document },
+ { doc: Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }) },
+ 'portal to:portal from'
+ );
}
- this.Document.followLinkLocation = OpenWhere.inPlace;
+ this.Document.followLinkLocation = OpenWhere.lightbox;
this.Document.followLinkZoom = true;
this.Document._isLinkButton = true;
};
@@ -955,12 +941,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
if (this.props.bringToFront !== emptyFunction) {
const zorders = cm.findByDescription('ZOrder...');
const zorderItems: ContextMenuProps[] = zorders && 'subitems' in zorders ? zorders.subitems : [];
- zorderItems.push({ description: 'Bring to Front', event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, false)), icon: 'expand-arrows-alt' });
- zorderItems.push({ description: 'Send to Back', event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: 'expand-arrows-alt' });
+ zorderItems.push({ description: 'Bring to Front', event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, false)), icon: 'arrow-up' });
+ zorderItems.push({ description: 'Send to Back', event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: 'arrow-down' });
zorderItems.push({
description: this.rootDoc._raiseWhenDragged !== false ? 'Keep ZIndex when dragged' : 'Allow ZIndex to change when dragged',
event: undoBatch(action(() => (this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined))),
- icon: 'expand-arrows-alt',
+ icon: 'hand-point-up',
});
!zorders && cm.addItem({ description: 'ZOrder...', noexpand: true, subitems: zorderItems, icon: 'compass' });
}
@@ -980,10 +966,8 @@ 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', 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.followLinkToggle ? 'Remove' : 'Make') + ' Target Visibility Toggle', event: () => this.toggleFollowLink(undefined, false, true), icon: 'map-pin' });
+ onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? 'Remove Click Behavior' : 'Follow Link', event: () => this.toggleFollowLink(false, false), icon: 'link' });
+ onClicks.push({ description: (this.Document.followLinkToggle ? 'Remove' : 'Make') + ' Target Visibility Toggle', event: () => this.toggleFollowLink(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) {
@@ -1151,7 +1135,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
className="documentView-contentsView"
style={{
pointerEvents:
- (this.props.pointerEvents?.() as any) ?? this.rootDoc.layoutKey === 'layout_icon'
+ this.opacity === 0
+ ? 'none'
+ : (this.props.pointerEvents?.() as any) ?? this.rootDoc.layoutKey === 'layout_icon'
? 'none'
: (this.props.contentPointerEvents as any)
? (this.props.contentPointerEvents as any)
@@ -1178,6 +1164,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
<DocumentContentsView
key={1}
{...this.props}
+ pointerEvents={this.opacity === 0 ? returnNone : this.props.pointerEvents}
docViewPath={this.props.viewPath}
thumbShown={this.thumbShown}
isHovering={this.props.isHovering}
@@ -1189,7 +1176,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
ScreenToLocalTransform={this.screenToLocal}
rootSelected={this.rootSelected}
onClick={this.onClickFunc}
- focus={this.focus}
+ focus={emptyFunction}
layoutKey={this.finalLayoutKey}
/>
{this.layoutDoc.hideAllLinks ? null : this.allLinkEndpoints}
@@ -1214,6 +1201,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
case StyleProp.ShowTitle: return '';
case StyleProp.PointerEvents: return 'none';
case StyleProp.LinkSource: return this.props.Document; // pass the LinkSource to the LinkAnchorBox
+ case StyleProp.Highlighting: return undefined;
}
return this.props.styleProvider?.(doc, props, property);
};
@@ -1229,8 +1217,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
link =>
Doc.AreProtosEqual(link.anchor1 as Doc, this.rootDoc) ||
Doc.AreProtosEqual(link.anchor2 as Doc, this.rootDoc) ||
- ((link.anchor1 as Doc).unrendered && Doc.AreProtosEqual((link.anchor1 as Doc).annotationOn as Doc, this.rootDoc)) ||
- ((link.anchor2 as Doc).unrendered && Doc.AreProtosEqual((link.anchor2 as Doc).annotationOn as Doc, this.rootDoc))
+ ((link.anchor1 as Doc)?.unrendered && Doc.AreProtosEqual((link.anchor1 as Doc)?.annotationOn as Doc, this.rootDoc)) ||
+ ((link.anchor2 as Doc)?.unrendered && Doc.AreProtosEqual((link.anchor2 as Doc)?.annotationOn as Doc, this.rootDoc))
);
}
@computed get allLinks() {
@@ -1458,7 +1446,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
* @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 dir = presEffectDoc?.presEffectDirection ?? presEffectDoc?.followLinkAnimDirection;
const effectProps = {
left: dir === PresEffectDirection.Left,
right: dir === PresEffectDirection.Right,
@@ -1466,7 +1454,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
bottom: dir === PresEffectDirection.Bottom,
opposite: true,
delay: 0,
- duration: Cast(presEffectDoc?.presTransition, 'number', null),
+ duration: Cast(presEffectDoc?.presTransition, 'number', Cast(presEffectDoc?.followLinkTransitionTime, 'number', null)),
};
//prettier-ignore
switch (StrCast(presEffectDoc?.presEffect, StrCast(presEffectDoc?.followLinkAnimEffect))) {
@@ -1640,7 +1628,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return this.docView?.LayoutFieldKey || 'layout';
}
get fitWidth() {
- return this.props.fitWidth?.(this.rootDoc) || this.layoutDoc.fitWidth;
+ return this.props.fitWidth?.(this.rootDoc) ?? this.layoutDoc?.fitWidth;
}
@computed get hideLinkButton() {
@@ -1688,8 +1676,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
@computed get panelHeight() {
if (this.effectiveNativeHeight && !this.layoutDoc.nativeHeightUnfrozen) {
- const scrollHeight = this.fitWidth ? Math.max(this.ComponentView?.getScrollHeight?.() ?? NumCast(this.layoutDoc.scrollHeight)) : 0;
- return Math.min(this.props.PanelHeight(), Math.max(scrollHeight, this.effectiveNativeHeight) * this.nativeScaling);
+ return Math.min(this.props.PanelHeight(), this.effectiveNativeHeight * this.nativeScaling);
}
return this.props.PanelHeight();
}
@@ -1709,7 +1696,6 @@ 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.type === DocumentType.PRES || this.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
return undefined;
@@ -1902,6 +1888,11 @@ ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) {
documentView.select(false);
});
+ScriptingGlobals.add(function deiconifyViewToLightbox(documentView: DocumentView) {
+ //documentView.iconify(() =>
+ LightboxView.AddDocTab(documentView.rootDoc, OpenWhere.lightbox, 'layout'); //, 0);
+});
+
ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
if (dv.Document.layoutKey === 'layout_' + detailLayoutKeySuffix) dv.switchViews(false, 'layout');
else dv.switchViews(true, detailLayoutKeySuffix, undefined, true);
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 78ba499ec..d41a4bb82 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -465,7 +465,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
isContentActive={returnTrue}
whenChildContentsActiveChanged={returnFalse}
treeViewHideTitle={true}
- focus={returnFalse}
+ focus={emptyFunction}
onCheckedClick={this.scriptField}
treeViewHideHeaderFields={false}
dontRegisterView={true}
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index 5c0005dae..75b4907cd 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -13,8 +13,9 @@ import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { undoBatch } from '../../util/UndoManager';
import { ViewBoxAnnotatableComponent } from '../DocComponent';
-import { DocFocusOptions } from './DocumentView';
+import { DocFocusOptions, DocumentView } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
+import { PinProps, PresBox } from './trails';
const EquationSchema = createSchema({});
@@ -37,23 +38,18 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
componentDidMount() {
this.props.setContentView?.(this);
reaction(
- () => [DocListCast(this.dataDoc[this.fieldKey]).map(doc => doc?.text), this.layoutDoc.width, this.layoutDoc.height, this.dataDoc.xRange, this.dataDoc.yRange],
+ () => [DocListCast(this.dataDoc[this.fieldKey]).map(doc => doc?.text), this.layoutDoc.width, this.layoutDoc.height, this.rootDoc.xRange, this.rootDoc.yRange],
() => this.createGraph()
);
}
- getAnchor = (addAsAnnotation: boolean) => {
- const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc });
- anchor.xRange = new List<number>(Array.from(this._plot.options.xAxis.domain));
- anchor.yRange = new List<number>(Array.from(this._plot.options.yAxis.domain));
+ getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
+ const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc, unrendered: true });
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), datarange: true } }, this.rootDoc);
+ anchor.presXRange = new List<number>(Array.from(this._plot.options.xAxis.domain));
+ anchor.presYRange = new List<number>(Array.from(this._plot.options.yAxis.domain));
if (addAsAnnotation) this.addDocument(anchor);
return anchor;
};
- @action
- 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;
- };
createGraph = (ele?: HTMLDivElement) => {
this._plotEle = ele || this._plotEle;
const width = this.props.PanelWidth();
@@ -65,8 +61,8 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
target: '#' + this._plotEle.id,
width,
height,
- xAxis: { domain: Cast(this.dataDoc.xRange, listSpec('number'), [-10, 10]) },
- yAxis: { domain: Cast(this.dataDoc.xRange, listSpec('number'), [-1, 9]) },
+ xAxis: { domain: Cast(this.rootDoc.xRange, listSpec('number'), [-10, 10]) },
+ yAxis: { domain: Cast(this.rootDoc.yRange, listSpec('number'), [-1, 9]) },
grid: true,
data: fns.map(fn => ({
fn,
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 1eaf86c56..60f799463 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -11,12 +11,13 @@ import { ComputedField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
+import { DashColor, 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 { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
import { Networking } from '../../Network';
+import { DocumentManager } from '../../util/DocumentManager';
import { DragManager } from '../../util/DragManager';
import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../../views/ContextMenu';
@@ -26,12 +27,13 @@ import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComp
import { MarqueeAnnotator } from '../MarqueeAnnotator';
import { AnchorMenu } from '../pdf/AnchorMenu';
import { StyleProp } from '../StyleProvider';
+import { OpenWhere } from './DocumentView';
import { FaceRectangles } from './FaceRectangles';
import { FieldView, FieldViewProps } from './FieldView';
import './ImageBox.scss';
+import { PinProps, PresBox } from './trails';
import React = require('react');
-import { PresBox } from './trails';
-import { DocFocusOptions, DocumentViewProps } from './DocumentView';
+import Color = require('color');
export const pageSchema = createSchema({
googlePhotosUrl: 'string',
@@ -50,6 +52,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(ImageBox, fieldKey);
}
+ private _ignoreScroll = false;
+ private _forcedScroll = false;
private _dropDisposer?: DragManager.DragDropDisposer;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _getAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = () => undefined;
@@ -66,33 +70,14 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.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 = (addAsAnnotation: boolean) => {
+ getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
const anchor =
this._getAnchor?.(this._savedAnnotations, false) ?? // use marquee anchor, otherwise, save zoom/pan as anchor
- Docs.Create.ImageanchorDocument({ presTransition: 1000, unrendered: true, annotationOn: this.rootDoc });
+ Docs.Create.ImageanchorDocument({ title: 'ImgAnchor:' + this.rootDoc.title, presTransition: 1000, unrendered: true, annotationOn: this.rootDoc });
if (anchor) {
- PresBox.pinDocView(anchor, { pinData: { pannable: true, dataview: true, dataannos: true } }, this.rootDoc);
- addAsAnnotation && this.addDocument(anchor);
+ if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
+ /* addAsAnnotation &&*/ this.addDocument(anchor);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: true } }, this.rootDoc);
return anchor;
}
return this.rootDoc;
@@ -118,6 +103,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
},
{ fireImmediately: true }
);
+ this._disposers.scroll = reaction(
+ () => this.layoutDoc._scrollTop,
+ s_top => {
+ this._forcedScroll = true;
+ !this._ignoreScroll && this._mainCont.current && (this._mainCont.current.scrollTop = NumCast(s_top));
+ this._mainCont.current?.scrollTo({ top: NumCast(s_top) });
+ this._forcedScroll = false;
+ },
+ { fireImmediately: true }
+ );
}
componentWillUnmount() {
@@ -155,6 +150,21 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
setUseAlt = () => (this.layoutDoc[this.fieldKey + '-useAlt'] = !this.layoutDoc[this.fieldKey + '-useAlt']);
@undoBatch
+ setNativeSize = action(() => {
+ const scaling = (this.props.DocumentView?.().props.ScreenToLocalTransform().Scale || 1) / NumCast(this.rootDoc._viewScale, 1);
+ const nscale = NumCast(this.props.PanelWidth()) / scaling;
+ const nh = nscale / NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']);
+ const nw = nscale / NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']);
+ this.dataDoc[this.fieldKey + '-nativeHeight'] = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']) * nh;
+ this.dataDoc[this.fieldKey + '-nativeWidth'] = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']) * nh;
+ this.rootDoc._panX = nh * NumCast(this.rootDoc._panX);
+ this.rootDoc._panY = nh * NumCast(this.rootDoc._panY);
+ this.dataDoc._panXMax = this.dataDoc._panXMax ? nh * NumCast(this.dataDoc._panXMax) : undefined;
+ this.dataDoc._panXMin = this.dataDoc._panXMin ? nh * NumCast(this.dataDoc._panXMin) : undefined;
+ this.dataDoc._panYMax = this.dataDoc._panYMax ? nw * NumCast(this.dataDoc._panYMax) : undefined;
+ this.dataDoc._panYMin = this.dataDoc._panYMin ? nw * NumCast(this.dataDoc._panYMin) : undefined;
+ });
+ @undoBatch
rotate = action(() => {
const nw = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']);
const nh = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']);
@@ -188,6 +198,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const croppingProto = Doc.GetProto(cropping);
croppingProto.annotationOn = undefined;
croppingProto.isPrototype = true;
+ croppingProto.backgroundColor = undefined;
croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
croppingProto.type = DocumentType.IMG;
croppingProto.layout = ImageBox.LayoutString('data');
@@ -204,7 +215,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
croppingProto.panYMax = anchh / viewScale;
if (addCrop) {
DocUtils.MakeLink({ doc: region }, { doc: cropping }, 'cropped image', '');
+ cropping.x = NumCast(this.rootDoc.x) + this.rootDoc[WidthSym]();
+ cropping.y = NumCast(this.rootDoc.y);
+ this.props.addDocTab(cropping, OpenWhere.inParent);
}
+ DocumentManager.Instance.AddViewRenderedCb(cropping, dv => setTimeout(() => (dv.ComponentView as ImageBox).setNativeSize(), 200));
this.props.bringToFront(cropping);
return cropping;
};
@@ -213,10 +228,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const field = Cast(this.dataDoc[this.fieldKey], ImageField);
if (field) {
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' });
+ funcs.push({ description: 'Rotate Clockwise 90', event: this.rotate, icon: 'redo-alt' });
+ funcs.push({ description: `Show ${this.layoutDoc._showFullRes ? 'Dynamic Res' : 'Full Res'}`, event: this.resolution, icon: 'expand' });
+ funcs.push({ description: 'Set Native Pixel Size', event: this.setNativeSize, icon: 'expand-arrows-alt' });
+ funcs.push({ description: `${this.layoutDoc[this.fieldKey + '-useAlt'] ? 'Show Alternate' : 'Show Primary'}`, event: this.setUseAlt, icon: 'image' });
+ funcs.push({ description: 'Copy path', event: () => Utils.CopyText(this.choosePath(field.url)), icon: 'copy' });
if (!Doc.noviceMode) {
funcs.push({ description: 'Export to Google Photos', event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: 'caret-square-right' });
@@ -281,6 +297,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
return !tags ? null : <img id={'google-tags'} src={'/assets/google_tags.png'} />;
};
+ getScrollHeight = () => (this.props.fitWidth?.(this.rootDoc) !== false && NumCast(this.rootDoc._viewScale, 1) === NumCast(this.rootDoc._viewScaleMin, 1) ? this.nativeSize.nativeHeight : undefined);
+
@computed
private get considerDownloadIcon() {
const data = this.dataDoc[this.fieldKey];
@@ -345,8 +363,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@computed get content() {
TraceMobx();
- const srcpath = this.paths[0];
- const fadepath = this.paths.lastElement();
+ const backAlpha = DashColor(this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor)).alpha();
+ const srcpath = this.layoutDoc.hideImage ? '' : this.paths[0];
+ const fadepath = this.layoutDoc.hideImage ? '' : this.paths.lastElement();
const { nativeWidth, nativeHeight, nativeOrientation } = this.nativeSize;
const rotation = NumCast(this.dataDoc[this.fieldKey + '-rotation']);
const aspect = rotation % 180 ? nativeHeight / nativeWidth : 1;
@@ -364,7 +383,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
return (
<div className="imageBox-cont" key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}>
- <div className="imageBox-fader" style={{ overflow: Array.from(this.props.docViewPath?.()).slice(-1)[0].fitWidth ? 'auto' : undefined }}>
+ <div className="imageBox-fader" style={{ opacity: backAlpha }}>
<img key="paths" src={srcpath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
{fadepath === srcpath ? null : (
<div
@@ -381,7 +400,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
);
}
- screenToLocalTransform = this.props.ScreenToLocalTransform;
contentFunc = () => [this.content];
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
@@ -392,6 +410,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
TraceMobx();
return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
}
+ screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop) * this.props.ScreenToLocalTransform().Scale);
marqueeDown = (e: React.PointerEvent) => {
if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale, 1) <= NumCast(this.rootDoc.viewScaleMin, 1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
setupMoveUpEvents(
@@ -415,11 +434,6 @@ 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();
const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
@@ -429,17 +443,27 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
className="imageBox"
onContextMenu={this.specificContextMenu}
ref={this._mainCont}
+ onScroll={action(e => {
+ if (!this._forcedScroll) {
+ if (this.layoutDoc._scrollTop || this._mainCont.current?.scrollTop) {
+ this._ignoreScroll = true;
+ this.layoutDoc._scrollTop = this._mainCont.current?.scrollTop;
+ this._ignoreScroll = false;
+ }
+ }
+ })}
style={{
width: this.props.PanelWidth() ? undefined : `100%`,
height: this.props.PanelWidth() ? undefined : `100%`,
pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
borderRadius,
+ overflow: this.layoutDoc.fitWidth || this.props.fitWidth?.(this.rootDoc) ? 'auto' : undefined,
}}>
<CollectionFreeFormView
{...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
renderDepth={this.props.renderDepth + 1}
fieldKey={this.annotationKey}
- styleProvider={this.styleProvider}
+ styleProvider={this.props.styleProvider}
CollectionView={undefined}
isAnnotationOverlay={true}
annotationLayerHostsContent={true}
@@ -447,6 +471,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
PanelHeight={this.props.PanelHeight}
ScreenToLocalTransform={this.screenToLocalTransform}
select={emptyFunction}
+ getScrollHeight={this.getScrollHeight}
NativeDimScaling={returnOne}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
removeDocument={this.removeDocument}
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index a2143f629..33fab6873 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -36,8 +36,6 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
this._timeout && clearTimeout(this._timeout);
}
- getAnchor = (addAsAnnotation: boolean) => 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);
}
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index eba9e281d..1b37dc7ab 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -60,6 +60,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
} else {
this.rootDoc[this.fieldKey + '_x'] = ((pt[0] - bounds.left) / bounds.width) * 100;
this.rootDoc[this.fieldKey + '_y'] = ((pt[1] - bounds.top) / bounds.height) * 100;
+ this.rootDoc.linkAutoMove = false;
}
}
return false;
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 1eab06381..bbe9b4323 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -253,7 +253,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
<DocumentView
ref={r => {
const targetanchor = this._linkDoc && this._linkSrc && LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
- targetanchor && this._targetDoc !== targetanchor && r?.focus(targetanchor, {});
+ targetanchor && this._targetDoc !== targetanchor && r?.props.focus?.(targetanchor, {});
}}
Document={this._targetDoc!}
moveDocument={returnFalse}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 95cb49037..c182dfd19 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,5 +1,6 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
+import BingMapsReact from 'bingmaps-react';
import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -43,20 +44,22 @@ const mapContainerStyle = {
};
const defaultCenter = {
- lat: 38.685,
- lng: -115.234,
+ lat: 42.360081,
+ lng: -71.058884,
};
const mapOptions = {
fullscreenControl: false,
};
+const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing Maps api key here: https://www.bingmapsportal.com/ and then add it to the .env file in the Dash-Web root directory as: _CLIENT_BING_MAPS=<your apikey>
const apiKey = process.env.GOOGLE_MAPS;
const script = document.createElement('script');
script.defer = true;
script.async = true;
script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`;
+console.log(script.src);
document.head.appendChild(script);
/**
@@ -84,6 +87,7 @@ const options = {
@observer
export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
+ static UseBing = true;
private _dropDisposer?: DragManager.DragDropDisposer;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
@@ -327,7 +331,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
docs.forEach(doc => {
if (doc.lat !== undefined && doc.lng !== undefined) {
const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng);
- doc.onClickBehavior = 'enterPortal';
if (existingMarker) {
Doc.AddDocToList(existingMarker, 'data', doc);
} else {
@@ -542,8 +545,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
// }
};
- panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
- panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
+ panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth();
+ panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
@@ -551,69 +554,99 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
infoHeight = () => this.props.PanelHeight() / 5;
anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
savedAnnotations = () => this._savedAnnotations;
+
+ _bingSearchManager: any;
+ _bingMap: any;
+ get MicrosoftMaps() {
+ return (window as any).Microsoft.Maps;
+ }
+ // uses Bing Search to retrieve lat/lng for a location. eg.,
+ // const results = this.geocodeQuery(map.map, 'Philadelphia, PA');
+ // to move the map to that location:
+ // const location = await this.geocodeQuery(this._bingMap, 'Philadelphia, PA');
+ // this._bingMap.current.setView({
+ // mapTypeId: this.MicrosoftMaps.MapTypeId.aerial,
+ // center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude),
+ // });
+ //
+ bingGeocode = (map: any, query: string) => {
+ return new Promise<{ latitude: number; longitude: number }>((res, reject) => {
+ //If search manager is not defined, load the search module.
+ if (!this._bingSearchManager) {
+ //Create an instance of the search manager and call the geocodeQuery function again.
+ this.MicrosoftMaps.loadModule('Microsoft.Maps.Search', () => {
+ this._bingSearchManager = new this.MicrosoftMaps.Search.SearchManager(map.current);
+ res(this.bingGeocode(map, query));
+ });
+ } else {
+ this._bingSearchManager.geocode({
+ where: query,
+ callback: action((r: any) => {
+ res(r.results[0].location);
+ }),
+ errorCallback: (e: any) => reject(),
+ });
+ }
+ });
+ };
+
+ bingViewOptions = {
+ center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng },
+ mapTypeId: 'grayscale',
+ };
+ bingMapOptions = {
+ navigationBarMode: 'square',
+ };
+ bingMapReady = (map: any) => (this._bingMap = map.map);
render() {
const renderAnnotations = (docFilters?: () => string[]) => null;
- // bcz: commmented this out. Otherwise, any documents that are rendered with an InfoWindow of a marker
- // will also be rendered as freeform annotations on the map. However, it doesn't seem that rendering
- // freeform documents on the map does anything anyway, so getting rid of it for now. Also, since documents
- // are rendered twice, adding a new note to the InfoWindow loses focus immediately on creation since it gets
- // shifted to the non-visible view of the document in this freeform view.
- // <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
- // renderDepth={this.props.renderDepth + 1}
- // isAnnotationOverlay={true}
- // fieldKey={this.annotationKey}
- // CollectionView={undefined}
- // setPreviewCursor={this.setPreviewCursor}
- // PanelWidth={this.panelWidth}
- // PanelHeight={this.panelHeight}
- // ScreenToLocalTransform={this.scrollXf}
- // scaling={returnOne}
- // dropAction={"alias"}
- // docFilters={docFilters || this.props.docFilters}
- // dontRenderDocuments={docFilters ? false : true}
- // select={emptyFunction}
- // bringToFront={emptyFunction}
- // whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- // removeDocument={this.removeDocument}
- // moveDocument={this.moveDocument}
- // addDocument={this.sidebarAddDocument}
- // childPointerEvents={"all"}
- // pointerEvents={Doc.ActiveTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
return (
<div className="mapBox" ref={this._ref}>
- {/*console.log(apiKey)*/}
- {/* <LoadScript
- googleMapsApiKey={apiKey!}
- libraries={['places', 'drawing']}
- > */}
- <div className="mapBox-wrapper" onWheel={e => e.stopPropagation()} onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()} style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}>
+ <div
+ className="mapBox-wrapper"
+ onWheel={e => e.stopPropagation()}
+ onPointerDown={async e => {
+ e.button === 0 && !e.ctrlKey && e.stopPropagation();
+ // just a simple test of bing maps geocode api
+ // const loc = await this.bingGeocode(this._bingMap, 'Philadelphia, PA');
+ // this._bingMap.current.setView({
+ // mapTypeId: this.MicrosoftMaps.MapTypeId.aerial,
+ // center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude),
+ // zoom: 15,
+ // });
+ }}
+ style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}>
<div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
{renderAnnotations(this.opaqueFilter)}
{SnappingManager.GetIsDragging() ? null : renderAnnotations()}
{this.annotationLayer}
- <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
- <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
- <input className="mapBox-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
- </Autocomplete>
-
- {this.renderMarkers()}
- {this.allMapMarkers
- .filter(marker => marker.infoWindowOpen)
- .map(marker => (
- <MapBoxInfoWindow
- key={marker[Id]}
- {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
- place={marker}
- markerMap={this.markerMap}
- PanelWidth={this.infoWidth}
- PanelHeight={this.infoHeight}
- moveDocument={this.moveDocument}
- isAnyChildContentActive={this.isAnyChildContentActive}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- />
- ))}
- {/* {this.handleMapCenter(this._map)} */}
- </GoogleMap>
+
+ {!MapBox.UseBing ? null : <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions} />}
+ <div style={{ display: MapBox.UseBing ? 'none' : undefined }}>
+ <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
+ <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
+ <input className="mapBox-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
+ </Autocomplete>
+
+ {this.renderMarkers()}
+ {this.allMapMarkers
+ .filter(marker => marker.infoWindowOpen)
+ .map(marker => (
+ <MapBoxInfoWindow
+ key={marker[Id]}
+ {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+ place={marker}
+ markerMap={this.markerMap}
+ PanelWidth={this.infoWidth}
+ PanelHeight={this.infoHeight}
+ moveDocument={this.moveDocument}
+ isAnyChildContentActive={this.isAnyChildContentActive}
+ whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+ />
+ ))}
+ {/* {this.handleMapCenter(this._map)} */}
+ </GoogleMap>
+ </div>
{!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
<MarqueeAnnotator
rootDoc={this.rootDoc}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 40c04c08f..7e0b5c4d3 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -5,12 +5,13 @@ 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 { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
+import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField, PdfField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
+import { DocumentManager } from '../../util/DocumentManager';
import { KeyCodes } from '../../util/KeyCodes';
import { undoBatch, UndoManager } from '../../util/UndoManager';
import { CollectionFreeFormView } from '../collections/collectionFreeForm';
@@ -22,13 +23,12 @@ import { LightboxView } from '../LightboxView';
import { CreateImage } from '../nodes/WebBoxRenderer';
import { PDFViewer } from '../pdf/PDFViewer';
import { SidebarAnnos } from '../SidebarAnnos';
+import { DocFocusOptions, DocumentView, OpenWhere } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { ImageBox } from './ImageBox';
import './PDFBox.scss';
-import { VideoBox } from './VideoBox';
+import { PinProps, PresBox } from './trails';
import React = require('react');
-import { PresBox } from './trails';
-import { DocFocusOptions } from './DocumentView';
@observer
export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
@@ -141,7 +141,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
4
)
.then((data_url: any) => {
- VideoBox.convertDataUri(data_url, region[Id]).then(returnedfilename =>
+ Utils.convertDataUri(data_url, region[Id]).then(returnedfilename =>
setTimeout(
action(() => {
croppingProto.data = new ImageField(returnedfilename);
@@ -198,21 +198,26 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
);
}
- brushView = (view: { width: number; height: number; panX: number; panY: number }) => {
- this._pdfViewer?.brushView(view);
- };
- scrollFocus = (doc: Doc, options: DocFocusOptions) => {
- let didToggle = false;
- if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
- this.toggleSidebar(!options.instant);
- didToggle = true;
+ brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._pdfViewer?.brushView(view);
+
+ sidebarAddDocTab = (doc: Doc, where: OpenWhere) => {
+ if (DocListCast(this.props.Document[this.props.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
+ this.toggleSidebar(false);
+ return true;
}
- if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
- this._initialScrollTarget = doc;
- 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);
+ return this.props.addDocTab(doc, where);
+ };
+ focus = (anchor: Doc, options: DocFocusOptions) => {
+ this._initialScrollTarget = anchor;
+ return this._pdfViewer?.scrollFocus(anchor, NumCast(anchor.y, NumCast(anchor.presViewScroll)), options);
};
- getAnchor = (addAsAnnotation: boolean) => {
+
+ getView = async (doc: Doc) => {
+ if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false);
+ return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)));
+ };
+
+ getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
let ele: Opt<HTMLDivElement> = undefined;
if (this._pdfViewer?.selectionContent()) {
ele = document.createElement('div');
@@ -222,12 +227,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const anchor = Docs.Create.TextanchorDocument({
title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)),
unrendered: true,
+ annotationOn: this.rootDoc,
});
- PresBox.pinDocView(anchor, { pinData: { scrollable: true, pannable: true } }, this.rootDoc);
return anchor;
};
const annoAnchor = this._pdfViewer?._getAnchor(this._pdfViewer.savedAnnotations(), true);
const anchor = annoAnchor ?? docAnchor();
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true, pannable: true } }, this.rootDoc);
anchor.text = ele?.textContent ?? '';
anchor.textHtml = ele?.innerHTML;
if (addAsAnnotation || annoAnchor) {
@@ -287,8 +293,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
setPdfViewer = (pdfViewer: PDFViewer) => {
this._pdfViewer = pdfViewer;
- if (this._initialScrollTarget) {
- this.scrollFocus(this._initialScrollTarget, { instant: true });
+ const docView = this.props.DocumentView?.();
+ if (this._initialScrollTarget && docView) {
+ this.focus(this._initialScrollTarget, { instant: true });
this._initialScrollTarget = undefined;
}
};
@@ -487,9 +494,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
{...this.props}
sidebarAddDoc={this.sidebarAddDocument}
rootDoc={this.rootDoc}
+ addDocTab={this.sidebarAddDocTab}
layoutDoc={this.layoutDoc}
dataDoc={this.dataDoc}
pdf={this._pdf!}
+ focus={this.focus}
url={this.pdfUrl!.url.pathname}
isContentActive={this.isPdfContentActive}
anchorMenuClick={this.anchorMenuClick}
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index f94996c66..61e4894f0 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -130,7 +130,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
}
getAnchor = (addAsAnnotation: boolean) => {
const startTime = Cast(this.layoutDoc._currentTimecode, 'number', null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
- return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow', '_timecodeToHide', startTime, startTime === undefined ? undefined : startTime + 3, undefined, addAsAnnotation) || this.rootDoc;
+ return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, startTime, startTime === undefined ? undefined : startTime + 3, undefined, addAsAnnotation) || this.rootDoc;
};
videoLoad = () => {
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 281967a21..fa2021642 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -20,6 +20,7 @@ import { EditableView } from '../EditableView';
import { FieldView, FieldViewProps } from '../nodes/FieldView';
import { OverlayView } from '../OverlayView';
import { DocumentIconContainer } from './DocumentIcon';
+import { DocFocusOptions, DocumentView } from './DocumentView';
import './ScriptingBox.scss';
const _global = (window /* browser */ || global) /* node */ as any;
@@ -481,10 +482,6 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
return value;
}
- scrollFocus = () => {
- return undefined;
- };
-
getSuggestedParams(pos: number) {
const firstScript = this.rawText.slice(0, pos);
const indexP = firstScript.lastIndexOf('.');
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 1dfa55c64..7df3d5f24 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -3,10 +3,10 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, untracked } from 'mobx';
import { observer } from 'mobx-react';
import { basename } from 'path';
-import * as rp from 'request-promise';
import { Doc, DocListCast, HeightSym, WidthSym } from '../../../fields/Doc';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
+import { ObjectField } from '../../../fields/ObjectField';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { AudioField, ImageField, VideoField } from '../../../fields/URLField';
import { emptyFunction, formatTime, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
@@ -27,11 +27,11 @@ import { DocumentDecorations } from '../DocumentDecorations';
import { MarqueeAnnotator } from '../MarqueeAnnotator';
import { AnchorMenu } from '../pdf/AnchorMenu';
import { StyleProp } from '../StyleProvider';
+import { OpenWhere } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { RecordingBox } from './RecordingBox';
+import { PinProps, PresBox } from './trails';
import './VideoBox.scss';
-import { ObjectField } from '../../../fields/ObjectField';
-import { DocFocusOptions, OpenWhere } from './DocumentView';
const path = require('path');
/**
@@ -51,30 +51,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(VideoBox, fieldKey);
}
- /**
- * Uploads an image buffer to the server and stores with specified filename. by default the image
- * is stored at multiple resolutions each retrieved by using the filename appended with _o, _s, _m, _l (indicating original, small, medium, or large)
- * @param imageUri the bytes of the image
- * @param returnedFilename the base filename to store the image on the server
- * @param nosuffix optionally suppress creating multiple resolution images
- */
- public static async convertDataUri(imageUri: string, returnedFilename: string, nosuffix = false, replaceRootFilename?: string) {
- try {
- const posting = Utils.prepend('/uploadURI');
- const returnedUri = await rp.post(posting, {
- body: {
- uri: imageUri,
- name: returnedFilename,
- nosuffix,
- replaceRootFilename,
- },
- json: true,
- });
- return returnedUri;
- } catch (e) {
- console.log('VideoBox :' + e);
- }
- }
static _youtubeIframeCounter: number = 0;
static heightPercent = 80; // height of video relative to videoBox when timeline is open
@@ -242,10 +218,20 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
}
+ _keepCurrentlyPlaying = false; // flag to prevent document when paused from being removed from global 'currentlyPlaying' list
+ IsPlaying = () => this._playing;
+ TogglePause = () => {
+ if (!this._playing) this.Play();
+ else {
+ this._keepCurrentlyPlaying = true;
+ this.pause();
+ setTimeout(() => (this._keepCurrentlyPlaying = false));
+ }
+ };
+
// pauses video
- @action public Pause = (update: boolean = true) => {
+ @action public pause = (update: boolean = true) => {
this._playing = false;
- this.removeCurrentlyPlaying();
try {
update && this.player?.pause();
update && this._audioPlayer?.pause();
@@ -263,6 +249,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
this._playRegionTimer = undefined;
};
+ @action Pause = (update: boolean = true) => {
+ this.pause(update);
+ !this._keepCurrentlyPlaying && this.removeCurrentlyPlaying();
+ };
// toggles video full screen
@action public FullScreen = () => {
@@ -354,7 +344,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, '');
const encodedFilename = encodeURIComponent('snapshot' + retitled + '_' + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, '_'));
const filename = basename(encodedFilename);
- VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) => returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY));
+ Utils.convertDataUri(dataUrl, filename).then((returnedFilename: string) => returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY));
}
};
@@ -390,13 +380,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
setTimeout(() => downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, 'move', true));
};
- getAnchor = (addAsAnnotation: boolean) => {
+ getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
const timecode = Cast(this.layoutDoc._currentTimecode, 'number', null);
const marquee = AnchorMenu.Instance.GetAnchor?.(undefined, addAsAnnotation);
- return (
- CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow' /* videoStart */, '_timecodeToHide' /* videoEnd */, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) ||
- this.rootDoc
- );
+ if (!addAsAnnotation && marquee) marquee.backgroundColor = 'transparent';
+ const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) || this.rootDoc;
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.rootDoc);
+ return anchor;
};
// sets video info on load
@@ -424,6 +414,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
};
+ // getView = async (doc: Doc) => {
+ // return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)));
+ // };
+
// extracts video thumbnails and saves them as field of doc
getVideoThumbnails = () => {
if (this.dataDoc.thumbnails !== undefined) return;
@@ -440,7 +434,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
canvas.getContext('2d')?.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, 100, 100);
const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, '');
const encodedFilename = encodeURIComponent('thumbnail' + retitled + '_' + video.currentTime.toString().replace(/\./, '_'));
- thumbnailPromises?.push(VideoBox.convertDataUri(canvas.toDataURL(), basename(encodedFilename), true));
+ thumbnailPromises?.push(Utils.convertDataUri(canvas.toDataURL(), basename(encodedFilename), true));
const newTime = video.currentTime + video.duration / (VideoBox.numThumbnails - 1);
if (newTime < video.duration) {
video.currentTime = newTime;
@@ -699,23 +693,24 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
);
};
- // removes video from currently playing display
+ // removes from currently playing display
@action
removeCurrentlyPlaying = () => {
- if (CollectionStackedTimeline.CurrentlyPlaying) {
- const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc);
+ const docView = this.props.DocumentView?.();
+ if (CollectionStackedTimeline.CurrentlyPlaying && docView) {
+ const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView);
index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
}
};
-
- // adds video to currently playing display
+ // adds doc to currently playing display
@action
addCurrentlyPlaying = () => {
+ const docView = this.props.DocumentView?.();
if (!CollectionStackedTimeline.CurrentlyPlaying) {
CollectionStackedTimeline.CurrentlyPlaying = [];
}
- if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc) === -1) {
- CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc);
+ if (docView && CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView) === -1) {
+ CollectionStackedTimeline.CurrentlyPlaying.push(docView);
}
};
@@ -953,14 +948,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
);
};
- scrollFocus = (doc: Doc, options: DocFocusOptions) => {
- if (doc !== this.rootDoc) {
- const showTime = Cast(doc._timecodeToShow, 'number', null);
- showTime !== undefined && setTimeout(() => this.Seek(showTime), 100);
- return 0.1;
- }
- };
-
// renders CollectionStackedTimeline
@computed get renderTimeline() {
return (
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 43b3b0536..5f9420a54 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,5 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as WebRequest from 'web-request';
import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
@@ -11,8 +11,9 @@ 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, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils';
+import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
+import { DocumentManager } from '../../util/DocumentManager';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SnappingManager } from '../../util/SnappingManager';
import { undoBatch, UndoManager } from '../../util/UndoManager';
@@ -21,7 +22,6 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm/Collec
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
-import { DocumentDecorations } from '../DocumentDecorations';
import { Colors } from '../global/globalEnums';
import { LightboxView } from '../LightboxView';
import { MarqueeAnnotator } from '../MarqueeAnnotator';
@@ -29,14 +29,14 @@ import { AnchorMenu } from '../pdf/AnchorMenu';
import { Annotation } from '../pdf/Annotation';
import { SidebarAnnos } from '../SidebarAnnos';
import { StyleProp } from '../StyleProvider';
-import { DocFocusOptions, DocumentView, DocumentViewInternal, DocumentViewProps } from './DocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { LinkDocPreview } from './LinkDocPreview';
-import { VideoBox } from './VideoBox';
+import { PinProps, PresBox } from './trails';
import './WebBox.scss';
import React = require('react');
import { DragManager } from '../../util/DragManager';
-import { gptSummarize } from '../../apis/gpt/Summarization';
+import { gptAPICall, GPTCallType } from '../../apis/gpt/Summarization';
import { GPTPopup } from '../pdf/GPTPopup';
const { CreateImage } = require('./WebBoxRenderer');
const _global = (window /* browser */ || global) /* node */ as any;
@@ -62,10 +62,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
// GPT Additions
private _summaryText: string = '';
- private _selectionText: string = '';
setSummaryText = async () => {
try {
- const summary = await gptSummarize(this.selectionText());
+ const summary = await gptAPICall(this.selectionText(), GPTCallType.SUMMARY);
this._summaryText = `Summary: ${summary}`;
} catch (err) {
console.log(err);
@@ -73,7 +72,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
};
summaryText = () => this._summaryText;
- selectionText = () => this._selectionText;
private get _getAnchor() {
return AnchorMenu.Instance?.GetAnchor;
@@ -107,7 +105,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return this.allAnnotations.filter(a => a.textInlineAnnotations);
}
@computed get webField() {
- return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url;
+ return Cast(this.rootDoc[this.props.fieldKey], WebField)?.url;
}
@computed get webThumb() {
return (
@@ -180,7 +178,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
console.log('BAD DATA IN THUMB CREATION');
return;
}
- VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + '-icon' + new Date().getTime(), true, this.layoutDoc[Id] + '-icon').then(returnedfilename =>
+ Utils.convertDataUri(data_url, this.layoutDoc[Id] + '-icon' + new Date().getTime(), true, this.layoutDoc[Id] + '-icon').then(returnedfilename =>
setTimeout(
action(() => {
this.rootDoc.thumbLockout = false;
@@ -205,9 +203,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this._annotationKeySuffix = () => this._urlHash + '-annotations';
const reqdFuncs: { [key: string]: string } = {};
// bcz: need to make sure that doc.data-annotations points to the currently active web page's annotations (this could/should be when the doc is created)
- reqdFuncs[this.fieldKey + '-annotations'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`;
- reqdFuncs[this.fieldKey + '-sidebar'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`;
- DocUtils.AssignScripts(this.dataDoc, {}, reqdFuncs);
+ reqdFuncs[this.fieldKey + '-annotations'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"])`;
+ reqdFuncs[this.fieldKey + '-annotations-setter'] = `this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"] = value`;
+ reqdFuncs[this.fieldKey + '-sidebar'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"])`;
+ DocUtils.AssignScripts(this.rootDoc, {}, reqdFuncs);
});
reaction(
() => this.props.isSelected(true) || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document),
@@ -274,6 +273,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
// this._iframe?.contentDocument?.removeEventListener("pointerup", this.iframeUp);
}
+ private _selectionText: string = '';
+ private _selectionContent: DocumentFragment | undefined;
+ selectionText = () => this._selectionText;
+ selectionContent = () => this._selectionContent;
@action
createTextAnnotation = (sel: Selection, selRange: Range | undefined) => {
if (this._mainCont.current && selRange) {
@@ -293,6 +296,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
}
//this._selectionText = selRange.cloneContents().textContent || "";
+ this._selectionContent = selRange?.cloneContents();
+ this._selectionText = this._selectionContent?.textContent || '';
// clear selection
if (sel.empty) sel.empty(); // Chrome
@@ -304,27 +309,35 @@ 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, 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(options.instant);
- }
- if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
- if (doc !== this.rootDoc && this._outerRef.current) {
+ focus = (anchor: Doc, options: DocFocusOptions) => {
+ if (anchor !== 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 = 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;
+ const scrollTo = Utils.scrollIntoView(NumCast(anchor.y), anchor[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + anchor[HeightSym](), this._scrollHeight));
+ if (scrollTo !== undefined) {
+ if (this._initialScroll === undefined) {
+ this.goTo(scrollTo, options.zoomTime ?? 500, options.easeFunc);
+ } else {
+ this._initialScroll = scrollTo;
+ }
}
}
- return undefined;
};
- getAnchor = (addAsAnnotation: boolean) => {
+ getView = async (doc: Doc) => {
+ if (this.rootDoc.layoutKey === 'layout_icon') this.props.DocumentView?.().iconify();
+ if (this._url && StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl));
+ if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false);
+ return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)));
+ };
+
+ sidebarAddDocTab = (doc: Doc, where: OpenWhere) => {
+ if (DocListCast(this.props.Document[this.props.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
+ this.toggleSidebar(false);
+ return true;
+ }
+ return this.props.addDocTab(doc, where);
+ };
+ getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
let ele: Opt<HTMLDivElement> = undefined;
try {
const contents = this._iframe?.contentWindow?.getSelection()?.getRangeAt(0).cloneContents();
@@ -339,10 +352,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
title: StrCast(this.rootDoc.title + ' ' + this.layoutDoc._scrollTop),
y: NumCast(this.layoutDoc._scrollTop),
unrendered: true,
+ annotationOn: this.rootDoc,
});
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true, pannable: true } }, this.rootDoc);
anchor.text = ele?.textContent ?? '';
anchor.textHtml = ele?.innerHTML;
- addAsAnnotation && this.addDocumentWrapper(anchor);
+ //addAsAnnotation &&
+ this.addDocumentWrapper(anchor);
return anchor;
};
@@ -398,9 +414,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
ContextMenu.Instance.setIgnoreEvents(true);
}
};
-
- getScrollHeight = () => this._scrollHeight;
-
isFirefox = () => {
return 'InstallTrigger' in window; // navigator.userAgent.indexOf("Chrome") !== -1;
};
@@ -545,14 +558,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
};
forward = (checkAvailable?: boolean) => {
- const future = Cast(this.dataDoc[this.fieldKey + '-future'], listSpec('string'), []);
- const history = Cast(this.dataDoc[this.fieldKey + '-history'], listSpec('string'), []);
+ const future = Cast(this.rootDoc[this.fieldKey + '-future'], listSpec('string'), []);
+ const history = Cast(this.rootDoc[this.fieldKey + '-history'], listSpec('string'), []);
if (checkAvailable) return future.length;
runInAction(() => {
if (future.length) {
const curUrl = this._url;
- this.dataDoc[this.fieldKey + '-history'] = new List<string>([...history, this._url]);
- this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!));
+ this.rootDoc[this.fieldKey + '-history'] = new List<string>([...history, this._url]);
+ this.rootDoc[this.fieldKey] = new WebField(new URL(future.pop()!));
if (this._webUrl === this._url) {
this._webUrl = curUrl;
setTimeout(action(() => (this._webUrl = this._url)));
@@ -566,15 +579,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
};
back = (checkAvailable?: boolean) => {
- const future = Cast(this.dataDoc[this.fieldKey + '-future'], listSpec('string'));
- const history = Cast(this.dataDoc[this.fieldKey + '-history'], listSpec('string'), []);
+ const future = Cast(this.rootDoc[this.fieldKey + '-future'], listSpec('string'));
+ const history = Cast(this.rootDoc[this.fieldKey + '-history'], listSpec('string'), []);
if (checkAvailable) return history.length;
runInAction(() => {
if (history.length) {
const curUrl = this._url;
- if (future === undefined) this.dataDoc[this.fieldKey + '-future'] = new List<string>([this._url]);
- else this.dataDoc[this.fieldKey + '-future'] = new List<string>([...future, this._url]);
- this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!));
+ if (future === undefined) this.rootDoc[this.fieldKey + '-future'] = new List<string>([this._url]);
+ else this.rootDoc[this.fieldKey + '-future'] = new List<string>([...future, this._url]);
+ this.layoutDoc[this.fieldKey] = new WebField(new URL(history.pop()!));
if (this._webUrl === this._url) {
this._webUrl = curUrl;
setTimeout(action(() => (this._webUrl = this._url)));
@@ -601,11 +614,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (!newUrl) return;
if (!newUrl.startsWith('http')) newUrl = 'http://' + newUrl;
try {
- const future = Cast(this.dataDoc[this.fieldKey + '-future'], listSpec('string'));
- const history = Cast(this.dataDoc[this.fieldKey + '-history'], listSpec('string'));
+ const future = Cast(this.rootDoc[this.fieldKey + '-future'], listSpec('string'));
+ const history = Cast(this.rootDoc[this.fieldKey + '-history'], listSpec('string'));
const url = this.webField?.toString();
if (url && !preview) {
- this.dataDoc[this.fieldKey + '-history'] = new List<string>([...(history || []), url]);
+ this.rootDoc[this.fieldKey + '-history'] = new List<string>([...(history || []), url]);
this.layoutDoc._scrollTop = 0;
if (this._webPageHasBeenRendered) {
this.layoutDoc.thumb = undefined;
@@ -616,7 +629,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
future && (future.length = 0);
}
if (!preview) {
- this.dataDoc[this.fieldKey] = new WebField(new URL(newUrl));
+ this.layoutDoc[this.fieldKey] = new WebField(new URL(newUrl));
!dontUpdateIframe && (this._webUrl = this._url);
}
} catch (e) {
@@ -742,7 +755,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@computed get urlContent() {
if (this._hackHide || (this.webThumb && !this._webPageHasBeenRendered && LightboxView.LightboxDoc !== this.rootDoc)) return null;
this.props.thumbShown?.();
- const field = this.dataDoc[this.props.fieldKey];
+ const field = this.rootDoc[this.props.fieldKey];
let view;
if (field instanceof HtmlField) {
view = <span className="webBox-htmlSpan" contentEditable onPointerDown={e => e.stopPropagation()} dangerouslySetInnerHTML={{ __html: field.html }} />;
@@ -850,7 +863,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
toggleSidebar = action((preview: boolean = false) => {
var nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
if (!nativeWidth) {
- const defaultNativeWidth = this.dataDoc[this.fieldKey] instanceof WebField ? 850 : this.Document[WidthSym]();
+ const defaultNativeWidth = this.rootDoc[this.fieldKey] instanceof WebField ? 850 : this.Document[WidthSym]();
Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || defaultNativeWidth);
Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || (this.Document[HeightSym]() / this.Document[WidthSym]()) * defaultNativeWidth);
nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
@@ -879,8 +892,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
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;
+ const interactive = !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None;
return (
<div className={'webBox-cont' + (interactive ? '-interactive' : '')} onKeyDown={e => e.stopPropagation()} style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']) || `100%` : '100%' }}>
{this.urlContent}
@@ -921,6 +933,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
PanelHeight={this.panelHeight}
ScreenToLocalTransform={this.scrollXf}
NativeDimScaling={returnOne}
+ focus={this.focus}
dropAction={'alias'}
docFilters={docFilters}
select={emptyFunction}
@@ -992,8 +1005,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => (this._searchString = e.currentTarget.value);
showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
- panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth; // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
- panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
+ panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth;
+ panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
@@ -1007,6 +1020,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
pointerEvents = () => (!this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance?.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none');
annotationPointerEvents = () => (this._isAnnotating || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None ? 'all' : 'none');
render() {
+ setTimeout(() => DocListCast(this.rootDoc[this.annotationKey]).forEach(doc => (doc.webUrl = this._url)));
const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
@@ -1039,9 +1053,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
docView={this.props.docViewPath().lastElement()}
finishMarquee={this.finishMarquee}
savedAnnotations={this.savedAnnotationsCreator}
- selectionText={returnEmptyString}
- summaryText={this.summaryText}
- setSummaryText={this.setSummaryText}
+ selectionText={this.selectionText}
annotationLayer={this._annotationLayer.current}
mainCont={this._mainCont.current}
/>
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 9ef32014b..0468d1c47 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -27,6 +27,7 @@ import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, SetAc
import { InkTranscription } from '../../InkTranscription';
import { StyleProp } from '../../StyleProvider';
import { FieldView, FieldViewProps } from '.././FieldView';
+import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentView';
import { OpenWhere } from '../DocumentView';
import { RichTextMenu } from '../formattedText/RichTextMenu';
import { WebBox } from '../WebBox';
@@ -90,7 +91,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
static GetRecognizeGestures() {
return BoolCast(Doc.UserDoc()._recognizeGestures);
}
- static SetRecognizeGesturs(show: boolean) {
+ static SetRecognizeGestures(show: boolean) {
Doc.UserDoc()._recognizeGestures = show;
}
@@ -136,7 +137,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
const setValue = (value: number) => UndoManager.RunInBatch(() => numScript?.script.run({ value, _readOnly_: false }), 'set num value');
// Script for checking the outcome of the toggle
- const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0;
+ const checkResult = Number(numScript?.script.run({ value: 0, _readOnly_: true }).result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3));
const label = !FontIconBox.GetShowLabels() ? null : <div className="fontIconBox-label">{this.label}</div>;
@@ -150,7 +151,6 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
max={NumCast(this.rootDoc.numBtnMax, 100)}
value={checkResult}
className={'menu-slider'}
- id="slider"
onPointerDown={() => (this._batch = UndoManager.StartBatch('presDuration'))}
onPointerUp={() => this._batch?.end()}
onChange={e => {
@@ -180,7 +180,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
className="list-item"
key={`${value}`}
style={{
- backgroundColor: value === checkResult ? Colors.LIGHT_BLUE : undefined,
+ backgroundColor: value.toString() === checkResult ? Colors.LIGHT_BLUE : undefined,
}}
onClick={() => setValue(value)}>
{value}
@@ -552,11 +552,30 @@ 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()?.props.Document ?? LinkManager.currentLink;
- if (checkResult) {
- return selected?._backgroundColor ?? 'transparent';
+ const selectedViews = SelectionManager.Views();
+ if (selectedViews.length) {
+ if (checkResult) {
+ const selView = selectedViews.lastElement();
+ const layoutFrameNumber = Cast(selView.props.ContainingCollectionDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
+ const contentFrameNumber = Cast(selView.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
+ return CollectionFreeFormDocumentView.getStringValues(selView?.rootDoc, contentFrameNumber).backgroundColor ?? 'transparent';
+ }
+ selectedViews.forEach(dv => {
+ const layoutFrameNumber = Cast(dv.props.ContainingCollectionDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
+ const contentFrameNumber = Cast(dv.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
+ if (contentFrameNumber !== undefined) {
+ CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.rootDoc, { backgroundColor: color });
+ } else {
+ dv.rootDoc._backgroundColor = color;
+ }
+ });
+ } else {
+ const selected = SelectionManager.Docs().length ? SelectionManager.Docs() : LinkManager.currentLink ? [LinkManager.currentLink] : [];
+ if (checkResult) {
+ return selected.lastElement()?._backgroundColor ?? 'transparent';
+ }
+ selected.forEach(doc => (doc._backgroundColor = color));
}
- if (selected) selected._backgroundColor = color;
});
// toggle: Set overlay status of selected document
@@ -642,13 +661,7 @@ ScriptingGlobals.add(function setFontHighlight(color?: string, checkResult?: boo
if (checkResult) {
return (selected ?? Doc.UserDoc())._fontHighlight;
}
- if (selected) {
- selected._fontColor = color;
- if (color) {
- editorView?.state && RichTextMenu.Instance.setHighlight(color, editorView, editorView?.dispatch);
- }
- }
- Doc.UserDoc()._fontHighlight = color;
+ color && RichTextMenu.Instance.setHighlight(color);
});
// toggle: Set overlay status of selected document
@@ -795,7 +808,7 @@ function setActiveTool(tool: InkTool | GestureUtils.Gestures, keepPrim: boolean,
GestureOverlay.Instance.KeepPrimitiveMode = keepPrim;
}
if (Object.values(GestureUtils.Gestures).includes(tool as any)) {
- if (GestureOverlay.Instance.InkShape === tool) {
+ if (GestureOverlay.Instance.InkShape === tool && !keepPrim) {
Doc.ActiveTool = InkTool.None;
GestureOverlay.Instance.InkShape = undefined;
} else {
@@ -804,7 +817,7 @@ function setActiveTool(tool: InkTool | GestureUtils.Gestures, keepPrim: boolean,
}
} else if (tool) {
// pen or eraser
- if (Doc.ActiveTool === tool && !GestureOverlay.Instance.InkShape) {
+ if (Doc.ActiveTool === tool && !GestureOverlay.Instance.InkShape && !keepPrim) {
Doc.ActiveTool = InkTool.None;
} else {
Doc.ActiveTool = tool as any;
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 722d0cc4f..19ee1fc7b 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -12,6 +12,7 @@ import { Transform } from '../../../util/Transform';
import { DocumentView } from '../DocumentView';
import { FormattedTextBox } from './FormattedTextBox';
import React = require('react');
+import { SelectionManager } from '../../../util/SelectionManager';
export class DashDocView {
dom: HTMLSpanElement; // container for label and value
@@ -192,10 +193,10 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
Document={this._finalLayout}
DataDoc={this._resolvedDataDoc}
addDocument={returnFalse}
- rootSelected={this._textBox.props.isSelected}
+ rootSelected={returnFalse} //{this._textBox.props.isSelected}
removeDocument={this.removeDoc}
isDocumentActive={returnFalse}
- isContentActive={this._textBox.props.isContentActive}
+ isContentActive={emptyFunction}
styleProvider={this._textBox.props.styleProvider}
docViewPath={this._textBox.props.docViewPath}
ScreenToLocalTransform={this.getDocTransform}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 39005a18b..b33529aeb 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -18,12 +18,18 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { CollectionViewType } from '../../../documents/DocumentTypes';
import { NodeSelection } from 'prosemirror-state';
import { OpenWhere } from '../DocumentView';
+import { FormattedTextBoxComment } from './FormattedTextBoxComment';
export class DashFieldView {
dom: HTMLDivElement; // container for label and value
root: any;
+ node: any;
+ tbox: FormattedTextBox;
+ unclickable = () => !this.tbox.props.isSelected() && this.node.marks.some((m: any) => m.type === this.tbox.EditorView?.state.schema.marks.linkAnchor && m.attrs.noPreview);
constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
+ this.node = node;
+ this.tbox = tbox;
this.dom = document.createElement('div');
this.dom.style.width = node.attrs.width;
this.dom.style.height = node.attrs.height;
@@ -44,7 +50,18 @@ export class DashFieldView {
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} />
+ <DashFieldViewInternal
+ node={node}
+ unclickable={this.unclickable}
+ 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() {
@@ -68,6 +85,7 @@ interface IDashFieldViewInternal {
editable: boolean;
node: any;
getPos: any;
+ unclickable: () => boolean;
}
@observer
@@ -125,7 +143,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
return (
<span
className="dashFieldView-fieldSpan"
- contentEditable={true}
+ contentEditable={!this.props.unclickable()}
style={{ display: strVal.length < 2 ? 'inline-block' : undefined }}
suppressContentEditableWarning={true}
defaultValue={strVal}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c6801bb79..60be9775c 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -45,7 +45,7 @@ import { LightboxView } from '../../LightboxView';
import { AnchorMenu } from '../../pdf/AnchorMenu';
import { SidebarAnnos } from '../../SidebarAnnos';
import { StyleProp } from '../../StyleProvider';
-import { DocFocusOptions, DocumentViewInternal, OpenWhere } from '../DocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewInternal, OpenWhere } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
import { LinkDocPreview } from '../LinkDocPreview';
import { DashDocCommentView } from './DashDocCommentView';
@@ -66,6 +66,7 @@ import React = require('react');
import { Configuration, OpenAIApi } from 'openai';
import { Networking } from '../../../Network';
import { gptAPICall, GPTCallType } from '../../../apis/gpt/Summarization';
+import { PinProps, PresBox } from '../trails';
const translateGoogleApi = require('translate-google-api');
export const GoogleRef = 'googleDocId';
@@ -84,7 +85,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
static _highlightStyleSheet: any = addStyleSheet();
static _bulletStyleSheet: any = addStyleSheet();
static _userStyleSheet: any = addStyleSheet();
- static _canAnnotate = true;
static _hadSelection: boolean = false;
private _sidebarRef = React.createRef<SidebarAnnos>();
private _ref: React.RefObject<HTMLDivElement> = React.createRef();
@@ -105,7 +105,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
private _rules: RichTextRules | undefined;
private _forceUncollapse = true; // if the cursor doesn't move between clicks, then the selection will disappear for some reason. This flags the 2nd click as happening on a selection which allows bullet points to toggle
private _forceDownNode: Node | undefined;
- private _downEvent: any;
private _downX = 0;
private _downY = 0;
private _break = true;
@@ -121,7 +120,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
@computed get noSidebar() {
- return this.props.docViewPath?.()[this.props.docViewPath().length - 2]?.rootDoc.type === DocumentType.RTF || this.props.noSidebar || this.Document._noSidebar;
+ return this.props.docViewPath().lastElement()?.props.hideDecorationTitle || this.props.noSidebar || this.Document._noSidebar;
}
@computed get sidebarWidthPercent() {
return this._showSidebar ? '20%' : StrCast(this.layoutDoc._sidebarWidthPercent, '0%');
@@ -238,7 +237,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
}
- getAnchor = (addAsAnnotation: boolean) => this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation);
+ getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
+ if (!pinProps && this._editorView?.state.selection.empty) return this.rootDoc;
+ const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc, unrendered: true });
+ this.addDocument(anchor);
+ this.makeLinkAnchor(anchor, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true } }, this.rootDoc);
+ return anchor;
+ };
@action
setupAnchorMenu = () => {
@@ -261,7 +267,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
});
};
AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => {
- this._editorView?.state && RichTextMenu.Instance.setHighlight(color, this._editorView, this._editorView?.dispatch);
+ this._editorView?.state && RichTextMenu.Instance.setHighlight(color);
return undefined;
});
AnchorMenu.Instance.onMakeAnchor = () => this.getAnchor(true);
@@ -316,7 +322,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
let unchanged = true;
if (this._applyingChange !== this.fieldKey && removeSelection(json) !== removeSelection(curProto?.Data)) {
this._applyingChange = this.fieldKey;
- curText !== Cast(dataDoc[this.fieldKey], RichTextField)?.Text && (dataDoc[this.fieldKey + '-lastModified'] = new DateField(new Date(Date.now())));
+ const textChange = curText !== Cast(dataDoc[this.fieldKey], RichTextField)?.Text;
+ textChange && (dataDoc[this.fieldKey + '-lastModified'] = new DateField(new Date(Date.now())));
if ((!curTemp && !curProto) || curText || json.includes('dash')) {
// if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
if (removeSelection(json) !== removeSelection(curLayout?.Data)) {
@@ -327,7 +334,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
dataDoc[this.fieldKey] = new RichTextField(json, curText);
}
dataDoc[this.fieldKey + '-noTemplate'] = true; //(curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
- ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText });
+ textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText });
unchanged = false;
}
} else {
@@ -790,13 +797,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
runInAction(() => (this.layoutDoc._highlights = FormattedTextBox._globalHighlights.join('')));
this.updateHighlights();
},
- icon: 'expand-arrows-alt',
+ icon: FormattedTextBox._globalHighlights.indexOf(option) === -1 ? 'highlighter' : 'remove-format',
})
);
const uicontrols: ContextMenuProps[] = [];
- !Doc.noviceMode && uicontrols.push({ description: `${FormattedTextBox._canAnnotate ? "Don't" : ''} Show Menu on Selections`, event: () => (FormattedTextBox._canAnnotate = !FormattedTextBox._canAnnotate), icon: 'expand-arrows-alt' });
- uicontrols.push({ description: !this.Document._noSidebar ? 'Hide Sidebar Handle' : 'Show Sidebar Handle', event: () => (this.layoutDoc._noSidebar = !this.layoutDoc._noSidebar), icon: 'expand-arrows-alt' });
+ uicontrols.push({ description: !this.Document._noSidebar ? 'Hide Sidebar Handle' : 'Show Sidebar Handle', event: () => (this.layoutDoc._noSidebar = !this.layoutDoc._noSidebar), icon: !this.Document._noSidebar ? 'eye-slash' : 'eye' });
uicontrols.push({ description: 'Show Highlights...', noexpand: true, subitems: highlighting, icon: 'hand-point-right' });
!Doc.noviceMode &&
uicontrols.push({
@@ -847,8 +853,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const optionItems = options && 'subitems' in options ? options.subitems : [];
optionItems.push({ description: `Generate Dall-E Image`, event: () => this.generateImage(), icon: 'star' });
optionItems.push({ description: `Ask GPT-3`, event: () => this.askGPT(), icon: 'lightbulb' });
- optionItems.push({ description: !this.Document._singleLine ? 'Make Single Line' : 'Make Multi Line', event: () => (this.layoutDoc._singleLine = !this.layoutDoc._singleLine), icon: 'expand-arrows-alt' });
- optionItems.push({ description: `${this.Document._autoHeight ? 'Lock' : 'Auto'} Height`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' });
+ optionItems.push({ description: !this.Document._singleLine ? 'Make Single Line' : 'Make Multi Line', event: () => (this.layoutDoc._singleLine = !this.layoutDoc._singleLine), icon: !this.Document._singleLine ? 'grip-lines' : 'bars' });
+ optionItems.push({ description: `${this.Document._autoHeight ? 'Lock' : 'Auto'} Height`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: this.Document._autoHeight ? 'lock' : 'unlock' });
!options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
this._downX = this._downY = Number.NaN;
};
@@ -1013,12 +1019,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
return anchorDoc ?? this.rootDoc;
}
- scrollFocus = (textAnchor: Doc, options: DocFocusOptions) => {
- let didToggle = false;
- if (DocListCast(this.Document[this.fieldKey + '-sidebar']).includes(textAnchor) && !this.SidebarShown) {
- this.toggleSidebar(options.instant);
- didToggle = true;
+ getView = async (doc: Doc) => {
+ if (DocListCast(this.rootDoc[this.SidebarKey]).find(anno => Doc.AreProtosEqual(doc.unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
+ !this.SidebarShown && this.toggleSidebar(false);
+ setTimeout(() => this._sidebarRef?.current?.makeDocUnfiltered(doc));
}
+ return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)));
+ };
+ focus = (textAnchor: Doc, options: DocFocusOptions) => {
+ const focusSpeed = options.zoomTime ?? 500;
const textAnchorId = textAnchor[Id];
const findAnchorFrag = (frag: Fragment, editor: EditorView) => {
const nodes: Node[] = [];
@@ -1057,7 +1066,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) {
- !options.instant && (this._focusSpeed = 500);
+ !options.instant && (this._focusSpeed = focusSpeed);
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
@@ -1069,11 +1078,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
setTimeout(() => clearStyleSheetRules(FormattedTextBox._highlightStyleSheet), Math.max(this._focusSpeed || 0, 3000));
}
}
-
- return this._didScroll ? this._focusSpeed : didToggle ? 1 : undefined; // if we actually scrolled, then return some focusSpeed
};
- getScrollHeight = () => this.scrollHeight;
// if the scroll height has changed and we're in autoHeight mode, then we need to update the textHeight component of the doc.
// Since we also monitor all component height changes, this will update the document's height.
resetNativeHeight = (scrollHeight: number) => {
@@ -1535,7 +1541,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
this._downX = e.clientX;
this._downY = e.clientY;
- this._downEvent = true;
FormattedTextBoxComment.textBox = this;
if (e.button === 0 && (this.props.rootSelected(true) || this.props.isSelected(true)) && !e.altKey && !e.ctrlKey && !e.metaKey) {
if (e.clientX < this.ProseRef!.getBoundingClientRect().right) {
@@ -1557,16 +1562,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
document.removeEventListener('pointermove', this.onSelectMove);
};
onPointerUp = (e: React.PointerEvent): void => {
- 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._editorView?.state.selection.empty && this.props.isContentActive(true) && !(e.nativeEvent as any).dash) {
- const editor = this._editorView!;
+ const editor = this._editorView!;
+ const state = editor?.state;
+ if (!state || !editor) return;
+ if (!state.selection.empty && !(state.selection instanceof NodeSelection)) this.setupAnchorMenu();
+ else if (this.props.isContentActive(true)) {
const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
- !this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0))));
+ !this.props.isSelected(true) && editor.dispatch(state.tr.setSelection(new TextSelection(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, target?.dataset.nopreview === 'true');
+ if (pcords && pcords.inside > 0 && state.doc.nodeAt(pcords.inside)?.type === state.schema.nodes.dashDoc) {
+ return;
+ }
}
if (e.button === 0 && this.props.isSelected(true) && !e.altKey) {
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index b70da2e5e..f0caa1f4f 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -54,7 +54,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@observable private _activeFontColor: string = 'black';
@observable private showColorDropdown: boolean = false;
- @observable private activeHighlightColor: string = 'transparent';
+ @observable private _activeHighlightColor: string = 'transparent';
@observable private showHighlightDropdown: boolean = false;
@observable private currentLink: string | undefined = '';
@@ -89,6 +89,9 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@computed get fontColor() {
return this._activeFontColor;
}
+ @computed get fontHighlight() {
+ return this._activeHighlightColor;
+ }
@computed get fontFamily() {
return this._activeFontFamily;
}
@@ -138,7 +141,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this._activeFontFamily = !activeFamilies.length ? StrCast(this.TextView?.Document.fontFamily, StrCast(Doc.UserDoc().fontFamily, 'Arial')) : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various';
this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, StrCast(Doc.UserDoc().fontSize, '10px')) : activeSizes[0];
this._activeFontColor = !activeColors.length ? StrCast(this.TextView?.Document.fontColor, StrCast(Doc.UserDoc().fontColor, 'black')) : activeColors.length > 0 ? String(activeColors[0]) : '...';
- this.activeHighlightColor = !activeHighlights.length ? '' : activeHighlights.length > 0 ? String(activeHighlights[0]) : '...';
+ this._activeHighlightColor = !activeHighlights.length ? '' : activeHighlights.length > 0 ? String(activeHighlights[0]) : '...';
// update link in current selection
this.getTextLinkTargetTitle().then(targetTitle => this.setCurrentLink(targetTitle));
@@ -356,11 +359,13 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this.updateMenu(this.view, undefined, this.props);
};
- setHighlight(color: String, view: EditorView, dispatch: any) {
- const highlightMark = view.state.schema.mark(view.state.schema.marks.marker, { highlight: color });
- if (view.state.selection.empty) return false;
- view.focus();
- this.setMark(highlightMark, view.state, dispatch, false);
+ setHighlight(color: string) {
+ if (this.view) {
+ const highlightMark = this.view.state.schema.mark(this.view.state.schema.marks.marker, { highlight: color });
+ this.setMark(highlightMark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(highlightMark)), true);
+ this.view.focus();
+ } else Doc.UserDoc()._fontHighlight = color;
+ this.updateMenu(this.view, undefined, this.props);
}
setColor(color: string) {
@@ -563,7 +568,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
}
@action setActiveHighlight(color: string) {
- this.activeHighlightColor = color;
+ this._activeHighlightColor = color;
}
@action setCurrentLink(link: string) {
diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss
index fd202590e..eb91c82f3 100644
--- a/src/client/views/nodes/trails/PresBox.scss
+++ b/src/client/views/nodes/trails/PresBox.scss
@@ -12,7 +12,7 @@
height: 100%;
min-height: 35px;
letter-spacing: 2px;
- overflow: hidden;
+ //overflow: hidden;
transition: 0.7s opacity ease;
.presBox-listCont {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 427911bd3..94962650a 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1,12 +1,12 @@
import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
-import { action, computed, IReactionDisposer, observable, ObservableSet, observe, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { ColorState, SketchPicker } from 'react-color';
-import { AnimationSym, Doc, DocListCast, FieldResult, HighlightSym, Opt, StrListCast } from '../../../../fields/Doc';
+import { AnimationSym, Doc, DocListCast, Field, FieldResult, Opt, StrListCast } from '../../../../fields/Doc';
import { Copy, Id } from '../../../../fields/FieldSymbols';
-import { InkTool } from '../../../../fields/InkField';
+import { InkField, InkTool } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
import { ObjectField } from '../../../../fields/ObjectField';
import { listSpec } from '../../../../fields/Schema';
@@ -23,20 +23,35 @@ import { SettingsManager } from '../../../util/SettingsManager';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
import { CollectionDockingView } from '../../collections/CollectionDockingView';
import { CollectionFreeFormView, computeTimelineLayout, MarqueeViewBounds } from '../../collections/collectionFreeForm';
+import { CollectionStackedTimeline } from '../../collections/CollectionStackedTimeline';
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, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
-import { CollectionStackedTimeline } from '../../collections/CollectionStackedTimeline';
+import { SerializationHelper } from '../../../util/SerializationHelper';
const { Howl } = require('howler');
+export interface pinDataTypes {
+ scrollable?: boolean;
+ pannable?: boolean;
+ viewType?: boolean;
+ inkable?: boolean;
+ filters?: boolean;
+ pivot?: boolean;
+ temporal?: boolean;
+ clippable?: boolean;
+ datarange?: boolean;
+ dataview?: boolean;
+ textview?: boolean;
+ poslayoutview?: boolean;
+ dataannos?: boolean;
+}
export interface PinProps {
audioRange?: boolean;
activeFrame?: number;
@@ -46,16 +61,7 @@ export interface PinProps {
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;
- };
+ pinData?: pinDataTypes;
}
@observer
@@ -211,7 +217,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
nextSlide = (slideNum?: number) => {
const nextSlideInd = slideNum ?? this.itemIndex + 1;
let curSlideInd = nextSlideInd;
- CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.());
+ CollectionStackedTimeline.CurrentlyPlaying?.map(clipView => clipView?.ComponentView?.Pause?.());
this.clearSelectedArray();
const doGroupWithUp =
(nextSelected: number, force = false) =>
@@ -295,7 +301,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (context) {
const ffview = DocumentManager.Instance.getFirstDocumentView(context)?.ComponentView as CollectionFreeFormView;
if (ffview?.childDocs) {
- this._keyTimer = CollectionFreeFormDocumentView.gotoKeyframe(this._keyTimer, ffview.childDocs.slice(), transTime);
+ this._keyTimer = CollectionFreeFormView.gotoKeyframe(this._keyTimer, ffview.childDocs, transTime);
context._currentFrame = NumCast(activeFrame);
}
}
@@ -317,59 +323,114 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
this.onHideDocument(); //Handles hide after/before
}
});
- static pinDataTypes(target?: Doc): { scrollable?: boolean; pannable?: boolean; temporal?: boolean; clippable?: boolean; dataview?: boolean; textview?: boolean; poslayoutview?: boolean; dataannos?: boolean } {
+ static pinDataTypes(target?: Doc): pinDataTypes {
const targetType = target?.type as any;
+ const inkable = [DocumentType.INK].includes(targetType);
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 datarange = [DocumentType.FUNCPLOT].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 viewType = targetType === DocumentType.COL;
+ const filters = true;
+ const pivot = true;
const dataannos = false;
- return { scrollable, pannable, temporal, clippable, dataview, textview, poslayoutview, dataannos };
+ return { scrollable, pannable, inkable, viewType, pivot, filters, temporal, clippable, dataview, textview, poslayoutview, dataannos };
}
@action
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;
+ static restoreTargetDocView(bestTargetView: Opt<DocumentView>, activeItem: Doc, transTime: number, pinDocLayout: boolean = BoolCast(activeItem.presPinLayout), pinDataTypes?: pinDataTypes, targetDoc?: Doc) {
+ const bestTarget = bestTargetView?.rootDoc ?? (targetDoc?.unrendered ? DocCast(targetDoc?.annotationOn) : targetDoc);
+ if (!bestTarget || activeItem === bestTarget) return;
let changed = false;
- if (pinProps?.pinDocLayout) {
+ if (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.rotation !== NumCast(activeItem.presRotation, 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.rotation = NumCast(activeItem.presRotation, 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;
+ if (pinDataTypes?.datarange || (!pinDataTypes && activeItem.presXRange !== undefined)) {
+ if (bestTarget.xRange !== activeItem.presXRange) {
+ bestTarget.xRange = (activeItem.presXRange as ObjectField)?.[Copy]();
+ changed = true;
+ }
+ if (bestTarget.yRange !== activeItem.presYRange) {
+ bestTarget.yRange = (activeItem.presYRange as ObjectField)?.[Copy]();
changed = true;
}
}
- if (pinDataTypes.temporal) {
+ if (pinDataTypes?.clippable || (!pinDataTypes && activeItem.presClipWidth !== undefined)) {
+ if (bestTarget._clipWidth !== activeItem.presClipWidth) {
+ bestTarget._clipWidth = activeItem.presClipWidth;
+ changed = true;
+ }
+ }
+ if (pinDataTypes?.temporal || (!pinDataTypes && activeItem.presStartTime !== undefined)) {
if (bestTarget._currentTimecode !== activeItem.presStartTime) {
bestTarget._currentTimecode = activeItem.presStartTime;
changed = true;
}
}
- if (pinDataTypes.scrollable) {
- if (bestTarget._scrollTop !== activeItem.presPinViewScroll) {
- bestTarget._scrollTop = activeItem.presPinViewScroll;
+ if (pinDataTypes?.inkable || (!pinDataTypes && (activeItem.presFillColor !== undefined || activeItem.color !== undefined))) {
+ if (bestTarget.fillColor !== activeItem.presFillColor) {
+ Doc.GetProto(bestTarget).fillColor = activeItem.presFillColor;
+ changed = true;
+ }
+ if (bestTarget.color !== activeItem.presColor) {
+ Doc.GetProto(bestTarget).color = activeItem.presColor;
+ changed = true;
+ }
+ if (bestTarget.width !== activeItem.width) {
+ bestTarget._width = NumCast(activeItem.presWidth, NumCast(bestTarget.width));
+ changed = true;
+ }
+ if (bestTarget.height !== activeItem.height) {
+ bestTarget._height = NumCast(activeItem.presHeight, NumCast(bestTarget.height));
+ changed = true;
+ }
+ }
+ if ((pinDataTypes?.viewType && activeItem.presViewType !== undefined) || (!pinDataTypes && activeItem.presViewType !== undefined)) {
+ if (bestTarget._viewType !== activeItem.presViewType) {
+ bestTarget._viewType = activeItem.presViewType;
+ changed = true;
+ }
+ }
+
+ if ((pinDataTypes?.filters && activeItem.presDocFilters !== undefined) || (!pinDataTypes && activeItem.presDocFilters !== undefined)) {
+ if (bestTarget.docFilters !== activeItem.presDocFilters) {
+ bestTarget.docFilters = ObjectField.MakeCopy(activeItem.presDocFilters as ObjectField) || new List<string>([]);
+ changed = true;
+ }
+ }
+
+ if ((pinDataTypes?.pivot && activeItem.presPivotField !== undefined) || (!pinDataTypes && activeItem.presPivotField !== undefined)) {
+ if (bestTarget.pivotField !== activeItem.presPivotField) {
+ bestTarget.pivotField = activeItem.presPivotField;
+ bestTarget._prevFilterIndex = 1; // need to revisit this...see CollectionTimeView
+ changed = true;
+ }
+ }
+
+ if (pinDataTypes?.scrollable || (!pinDataTypes && activeItem.presViewScroll !== undefined)) {
+ if (bestTarget._scrollTop !== activeItem.presViewScroll) {
+ bestTarget._scrollTop = activeItem.presViewScroll;
changed = true;
const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
if (contentBounds) {
@@ -378,37 +439,59 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
}
}
- if (pinDataTypes.dataannos) {
+ if (pinDataTypes?.dataannos || (!pinDataTypes && activeItem.presAnnotations !== undefined)) {
const fkey = Doc.LayoutFieldKey(bestTarget);
- Doc.GetProto(bestTarget)[fkey + '-annotations'] = new List<Doc>([...DocListCast(bestTarget[fkey + '-annotations']).filter(doc => doc.unrendered), ...DocListCast(activeItem.presAnnotations)]);
+ const oldItems = DocListCast(bestTarget[fkey + '-annotations']).filter(doc => doc.unrendered);
+ const newItems = DocListCast(activeItem.presAnnotations).map(doc => {
+ doc.hidden = false;
+ return doc;
+ });
+ const hiddenItems = DocListCast(bestTarget[fkey + '-annotations'])
+ .filter(doc => !doc.unrendered && !newItems.includes(doc))
+ .map(doc => {
+ doc.hidden = true;
+ return doc;
+ });
+ const newList = new List<Doc>([...oldItems, ...hiddenItems, ...newItems]);
+ Doc.GetProto(bestTarget)[fkey + '-annotations'] = newList;
}
- if (pinDataTypes.dataview && activeItem.presData !== undefined) {
+ if ((pinDataTypes?.dataview && activeItem.presData !== undefined) || (!pinDataTypes && activeItem.presData !== undefined)) {
+ bestTarget._dataTransition = `all ${transTime}ms`;
const fkey = Doc.LayoutFieldKey(bestTarget);
Doc.GetProto(bestTarget)[fkey] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
bestTarget[fkey + '-useAlt'] = activeItem.presUseAlt;
+ setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
}
- if (pinDataTypes.textview && activeItem.presData !== undefined) Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
- if (pinDataTypes.poslayoutview) {
+ if ((pinDataTypes?.textview && activeItem.presData !== undefined) || (!pinDataTypes && activeItem.presData !== undefined)) {
+ Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
+ }
+ if (pinDataTypes?.poslayoutview || (!pinDataTypes && activeItem.presPinLayoutData !== undefined)) {
changed = true;
+ const layoutField = Doc.LayoutFieldKey(bestTarget);
+ const transitioned = new Set<Doc>();
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 = `all ${transTime}ms`;
- doc.x = data.x;
- doc.y = data.y;
- doc._width = data.w;
- doc._height = data.h;
+ .map(str => JSON.parse(str) as { id: string; x: number; y: number; back: string; fill: string; w: number; h: number; data: string; text: string })
+ .forEach(async data => {
+ const doc = DocCast(DocServer.GetCachedRefField(data.id));
+ if (doc) {
+ transitioned.add(doc);
+ const field = !data.data ? undefined : await SerializationHelper.Deserialize(data.data);
+ const tfield = !data.text ? undefined : await SerializationHelper.Deserialize(data.text);
+ doc._dataTransition = `all ${transTime}ms`;
+ doc.x = data.x;
+ doc.y = data.y;
+ data.back && (doc._backgroundColor = data.back);
+ data.fill && (doc._fillColor = data.fill);
+ doc._width = data.w;
+ doc._height = data.h;
+ data.data && (Doc.GetProto(doc).data = field);
+ data.text && (Doc.GetProto(doc).text = tfield);
+ Doc.AddDocToList(Doc.GetProto(bestTarget), layoutField, doc);
+ }
});
- setTimeout(
- () =>
- StrListCast(activeItem.presPinLayoutData)
- .map(str => JSON.parse(str) as { id: string; x: number; y: number; w: number; h: number })
- .forEach(action(data => ((DocServer.GetCachedRefField(data.id) as Doc)._dataTransition = undefined))),
- transTime + 10
- );
+ setTimeout(() => Array.from(transitioned).forEach(action(doc => (doc._dataTransition = undefined))), transTime + 10);
}
- if (pinDataTypes.pannable) {
+ if (pinDataTypes?.pannable || (!pinDataTypes && (activeItem.presPinViewBounds !== undefined || activeItem.presPanX !== undefined || activeItem.presViewScale !== undefined))) {
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] };
@@ -421,16 +504,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
dv.ComponentView?.brushView?.(viewport);
}
} else {
- 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;
+ if (bestTarget._panX !== activeItem.presPanX || bestTarget._panY !== activeItem.presPanY || bestTarget._viewScale !== activeItem.presViewScale) {
+ bestTarget._panX = activeItem.presPanX;
+ bestTarget._panY = activeItem.presPanY;
+ bestTarget._viewScale = activeItem.presViewScale;
changed = true;
}
}
}
if (changed) {
- return bestTargetView.setViewTransition('all', transTime);
+ return bestTargetView?.setViewTransition('all', transTime);
}
}
@@ -443,7 +526,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
pinDoc.presPinLayout = true;
pinDoc.presX = NumCast(targetDoc.x);
pinDoc.presY = NumCast(targetDoc.y);
- pinDoc.presRot = NumCast(targetDoc.rotation);
+ pinDoc.presRotation = NumCast(targetDoc.rotation);
pinDoc.presWidth = NumCast(targetDoc.width);
pinDoc.presHeight = NumCast(targetDoc.height);
}
@@ -453,13 +536,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
pinProps.pinData.scrollable ||
pinProps.pinData.temporal ||
pinProps.pinData.pannable ||
+ pinProps.pinData.viewType ||
pinProps.pinData.clippable ||
+ pinProps.pinData.datarange ||
pinProps.pinData.dataview ||
pinProps.pinData.textview ||
pinProps.pinData.poslayoutview ||
pinProps?.activeFrame !== undefined;
+ const fkey = Doc.LayoutFieldKey(targetDoc);
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;
}
@@ -468,13 +553,41 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
pinDoc.presAnnotations = new List<Doc>(DocListCast(Doc.GetProto(targetDoc)[fkey + '-annotations']).filter(doc => !doc.unrendered));
}
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.inkable) {
+ pinDoc.presFillColor = targetDoc.fillColor;
+ pinDoc.presColor = targetDoc.color;
+ pinDoc.presWidth = targetDoc._width;
+ pinDoc.presHeight = targetDoc._height;
+ }
+ if (pinProps.pinData.scrollable) pinDoc.presViewScroll = targetDoc._scrollTop;
+ if (pinProps.pinData.clippable) pinDoc.presClipWidth = targetDoc._clipWidth;
+ if (pinProps.pinData.datarange) {
+ pinDoc.presXRange = undefined; //targetDoc?.xrange;
+ pinDoc.presYRange = undefined; //targetDoc?.yrange;
+ }
+ if (pinProps.pinData.poslayoutview)
+ pinDoc.presPinLayoutData = new List<string>(
+ DocListCast(targetDoc[fkey] as ObjectField).map(d =>
+ JSON.stringify({
+ id: d[Id],
+ x: NumCast(d.x),
+ y: NumCast(d.y),
+ w: NumCast(d._width),
+ h: NumCast(d._height),
+ fill: StrCast(d._fillColor),
+ back: StrCast(d._backgroundColor),
+ data: SerializationHelper.Serialize(d.data instanceof ObjectField ? d.data[Copy]() : ''),
+ text: SerializationHelper.Serialize(d.text instanceof ObjectField ? d.text[Copy]() : ''),
+ })
+ )
+ );
+ if (pinProps.pinData.viewType) pinDoc.presViewType = targetDoc._viewType;
+ if (pinProps.pinData.filters) pinDoc.presDocFilters = ObjectField.MakeCopy(targetDoc.docFilters as ObjectField);
+ if (pinProps.pinData.pivot) pinDoc.presPivotField = targetDoc._pivotField;
if (pinProps.pinData.pannable) {
- pinDoc.presPinViewX = NumCast(targetDoc._panX);
- pinDoc.presPinViewY = NumCast(targetDoc._panY);
- pinDoc.presPinViewScale = NumCast(targetDoc._viewScale, 1);
+ pinDoc.presPanX = NumCast(targetDoc._panX);
+ pinDoc.presPanY = NumCast(targetDoc._panY);
+ pinDoc.presViewScale = NumCast(targetDoc._viewScale, 1);
}
if (pinProps.pinData.temporal) {
pinDoc.presStartTime = targetDoc._currentTimecode;
@@ -486,9 +599,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
// If pinWithView option set then update scale and x / y props of slide
const bounds = pinProps.pinViewport;
pinDoc.presPinView = true;
- pinDoc.presPinViewScale = NumCast(targetDoc._viewScale, 1);
- pinDoc.presPinViewX = bounds.left + bounds.width / 2;
- pinDoc.presPinViewY = bounds.top + bounds.height / 2;
+ pinDoc.presViewScale = NumCast(targetDoc._viewScale, 1);
+ pinDoc.presPanX = bounds.left + bounds.width / 2;
+ pinDoc.presPanY = bounds.top + bounds.height / 2;
pinDoc.presPinViewBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
}
}
@@ -522,23 +635,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
finished();
});
- const createDocView = (doc: Doc, finished?: () => void) => {
- DocumentManager.Instance.AddViewRenderedCb(doc, () => finished?.());
- LightboxView.AddDocTab(doc, OpenWhere.lightbox);
- };
- PresBox.NavigateToTarget(targetDoc, activeItem, createDocView, resetSelection);
+ PresBox.NavigateToTarget(targetDoc, activeItem, resetSelection);
};
- static NavigateToTarget(targetDoc: Doc, activeItem: Doc, createDocView: any, finished?: () => void) {
+ static NavigateToTarget(targetDoc: Doc, activeItem: Doc, finished?: () => void) {
if (activeItem.presMovement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) {
(DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.();
return;
}
+ const effect = activeItem.presEffect && activeItem.presEffect !== PresEffect.None ? activeItem.presEffect : undefined;
+ const presTime = NumCast(activeItem.presTransition, effect ? 750 : 500);
const options: DocFocusOptions = {
willPan: activeItem.presMovement !== PresMovement.None,
- willPanZoom: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center,
+ willZoomCentered: 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),
+ zoomTime: activeItem.presMovement === PresMovement.Jump ? 0 : Math.min(Math.max(effect ? 750 : 500, (effect ? 0.2 : 1) * presTime), presTime),
effect: activeItem,
noSelect: true,
originatingDoc: activeItem,
@@ -546,38 +657,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
zoomTextSelections: BoolCast(activeItem.presZoomText),
playAudio: BoolCast(activeItem.presPlayAudio),
};
- 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));
- }
- };
- const finishAndRestoreLayout = () => {
- finished?.();
- restoreLayout();
- };
- const containerDocContext = DocumentManager.GetContextPath(targetDoc);
-
- let context = containerDocContext.length ? containerDocContext[0] : targetDoc;
if (activeItem.presOpenInLightbox) {
- if (!DocumentManager.Instance.getLightboxDocumentView(DocCast(DocCast(targetDoc.annotationOn) ?? targetDoc))) {
- context = DocCast(targetDoc.annotationOn) ?? targetDoc;
- LightboxView.SetLightboxDoc(context); // openInTab(targetDoc);
+ const context = DocCast(targetDoc.annotationOn) ?? targetDoc;
+ if (!DocumentManager.Instance.getLightboxDocumentView(context)) {
+ LightboxView.SetLightboxDoc(context);
}
}
if (targetDoc) {
if (activeItem.presentationTargetDoc instanceof Doc) activeItem.presentationTargetDoc[AnimationSym] = undefined;
DocumentManager.Instance.AddViewRenderedCb(LightboxView.LightboxDoc, dv => {
- if (!DocumentManager.Instance.getLightboxDocumentView(DocCast(context.annotationOn) ?? context)) {
+ // if target or the doc it annotates is not in the lightbox, then close the lightbox
+ if (!DocumentManager.Instance.getLightboxDocumentView(DocCast(targetDoc.annotationOn) ?? targetDoc)) {
LightboxView.SetLightboxDoc(undefined);
}
- DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finishAndRestoreLayout);
+ DocumentManager.Instance.showDocument(targetDoc, options, finished);
});
- } else finishAndRestoreLayout();
+ } else finished?.();
}
/**
@@ -595,7 +691,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
tagDoc.opacity = 1;
}
}
- const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex);
+ const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex) ?? itemIndexes.slice().reverse().lastElement();
if (curDoc.presHideBefore && index === hidingIndBef) {
if (index > this.itemIndex) {
tagDoc.opacity = 0;
@@ -603,10 +699,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
tagDoc.opacity = 1;
}
}
- const hidingIndAft = itemIndexes
- .slice()
- .reverse()
- .find(item => item < this.itemIndex);
+ const hidingIndAft =
+ itemIndexes
+ .slice()
+ .reverse()
+ .find(item => item <= this.itemIndex) ?? itemIndexes.lastElement();
if (curDoc.presHideAfter && index === hidingIndAft) {
if (index < this.itemIndex) {
tagDoc.opacity = 0;
@@ -625,16 +722,43 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
_exitTrail: Opt<() => void>;
PlayTrail = (docs: Doc[]) => {
- const savedStates = docs.map(doc => (doc._viewType !== CollectionViewType.Freeform ? undefined : { c: doc, x: NumCast(doc.panX), y: NumCast(doc.panY), s: NumCast(doc.viewScale) }));
+ const savedStates = docs.map(doc => {
+ switch (doc.type) {
+ case DocumentType.COL:
+ if (doc._viewType === CollectionViewType.Freeform) return { type: CollectionViewType.Freeform, doc, x: NumCast(doc.panX), y: NumCast(doc.panY), s: NumCast(doc.viewScale) };
+ break;
+ case DocumentType.INK:
+ if (doc.data instanceof InkField) {
+ return { type: doc.type, doc, data: doc.data?.[Copy](), fillColor: doc.fillColor, color: doc.color, x: NumCast(doc.x), y: NumCast(doc.y) };
+ }
+ }
+ return undefined;
+ });
this.startPresentation(0);
this._exitTrail = () => {
savedStates
.filter(savedState => savedState)
.map(savedState => {
- const { x, y, s, c } = savedState!;
- c._panX = x;
- c._panY = y;
- c._viewScale = s;
+ switch (savedState?.type) {
+ case CollectionViewType.Freeform:
+ {
+ const { x, y, s, doc } = savedState!;
+ doc._panX = x;
+ doc._panY = y;
+ doc._viewScale = s;
+ }
+ break;
+ case DocumentType.INK:
+ {
+ const { data, fillColor, color, x, y, doc } = savedState!;
+ doc.x = x;
+ doc.y = y;
+ doc.data = data;
+ doc.fillColor = fillColor;
+ doc.color = color;
+ }
+ break;
+ }
});
LightboxView.SetLightboxDoc(undefined);
Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc);
@@ -866,13 +990,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
presDocView && SelectionManager.SelectView(presDocView, false);
};
+ focusElement = (doc: Doc, options: DocFocusOptions) => {
+ this.selectElement(doc);
+ return undefined;
+ };
+
//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) setTimeout(() => this.updateCurrentPresentation(DocCast(doc.context)), 0);
- // else
+ selectElement = (doc: Doc, noNav = false) => {
+ CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => clip?.ComponentView?.Pause?.());
+ if (noNav) {
+ const index = this.childDocs.indexOf(doc);
+ if (index >= 0 && index < this.childDocs.length) {
+ this.rootDoc._itemIndex = index;
+ }
+ } else this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
this.updateCurrentPresentation(DocCast(doc.context));
};
@@ -908,19 +1040,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
//regular click
@action
- regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, selectPres = true) => {
+ regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, noNav: boolean, selectPres = true) => {
this.clearSelectedArray();
this.addToSelectedArray(doc);
this._eleArray.splice(0, this._eleArray.length, ref);
this._dragArray.splice(0, this._dragArray.length, drag);
- focus && this.selectElement(doc);
+ this.selectElement(doc, noNav);
selectPres && this.selectPres();
};
- modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, cmdClick: boolean, shiftClick: boolean) => {
+ modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, noNav: boolean, cmdClick: boolean, shiftClick: boolean) => {
if (cmdClick) this.multiSelect(doc, ref, drag);
else if (shiftClick) this.shiftSelect(doc, ref, drag);
- else this.regularSelect(doc, ref, drag, focus);
+ else this.regularSelect(doc, ref, drag, noNav);
};
static keyEventsWrapper = (e: KeyboardEvent) => PresBox.Instance?.keyEvents(e);
@@ -1061,15 +1193,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
} else if (doc.presPinView && presCollection === tagDoc && dv) {
// Case B: Document is presPinView and is presCollection
- const scale: number = 1 / NumCast(doc.presPinViewScale);
+ const scale: number = 1 / NumCast(doc.presViewScale);
const height: number = dv.props.PanelHeight() * scale;
const width: number = dv.props.PanelWidth() * scale;
const indWidth = width / 10;
const indHeight = Math.max(height / 10, 15);
const indEdge = Math.max(indWidth, indHeight);
const indFontSize = indEdge * 0.8;
- const xLoc: number = NumCast(doc.presPinViewX) - width / 2;
- const yLoc: number = NumCast(doc.presPinViewY) - height / 2;
+ const xLoc: number = NumCast(doc.presPanX) - width / 2;
+ const yLoc: number = NumCast(doc.presPanY) - height / 2;
docs.push(tagDoc);
order.push(
<>
@@ -1102,8 +1234,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if ((index = 0)) pathPoints = n1x + ',' + n1y;
else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
} else if (doc.presPinView) {
- const n1x = NumCast(doc.presPinViewX);
- const n1y = NumCast(doc.presPinViewY);
+ const n1x = NumCast(doc.presPanX);
+ const n1y = NumCast(doc.presPanY);
if ((index = 0)) pathPoints = n1x + ',' + n1y;
else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
}
@@ -1829,12 +1961,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
);
}
- scrollFocus = () => {
- // this.gotoDocument(0);
- // this.startOrPause(false);
- return undefined;
- };
-
_keyTimer: NodeJS.Timeout | undefined;
/**
@@ -1910,9 +2036,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const propTitle = SettingsManager.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel';
const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
const isMini: boolean = this.toolbarWidth <= 100;
+ const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc);
const activeColor = Colors.LIGHT_BLUE;
const inactiveColor = Colors.WHITE;
- return mode === CollectionViewType.Carousel3D ? null : (
+ return mode === CollectionViewType.Carousel3D || inOverlay ? null : (
<div id="toolbarContainer" className={'presBox-toolbar'}>
{/* <Tooltip title={<><div className="dash-tooltip">{"Add new slide"}</div></>}><div className={`toolbar-button ${this.newDocumentTools ? "active" : ""}`} onClick={action(() => this.newDocumentTools = !this.newDocumentTools)}>
<FontAwesomeIcon icon={"plus"} />
@@ -2244,12 +2371,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
ignoreUnrendered={true}
//childFitWidth={returnTrue}
childOpacity={returnOne}
+ //childLayoutString={PresElementBox.LayoutString('data')}
childLayoutTemplate={this.childLayoutTemplate}
childXPadding={Doc.IsComicStyle(this.rootDoc) ? 20 : undefined}
filterAddDocument={this.addDocumentFilter}
removeDocument={returnFalse}
dontRegisterView={true}
- focus={this.selectElement}
+ focus={this.focusElement}
scriptContext={this}
ScreenToLocalTransform={this.getTransform}
AddToMap={this.AddToMap}
@@ -2272,11 +2400,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
);
}
static NavigateToDoc(bestTarget: Doc, activeItem: Doc) {
- const openInTab = (doc: Doc, finished?: () => void) => {
- CollectionDockingView.AddSplit(doc, OpenWhereMod.right);
- finished?.();
- };
- PresBox.NavigateToTarget(bestTarget, activeItem, openInTab);
+ PresBox.NavigateToTarget(bestTarget, activeItem);
}
}
diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss
index 415253af1..4f95f0c1f 100644
--- a/src/client/views/nodes/trails/PresElementBox.scss
+++ b/src/client/views/nodes/trails/PresElementBox.scss
@@ -44,6 +44,12 @@ $slide-active: #5b9fdd;
display: flex;
align-items: center;
+ .presItem-number {
+ cursor: pointer;
+ &:hover {
+ background-color: $light-blue;
+ }
+ }
.presItem-name {
display: flex;
min-width: 20px;
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 788900b46..698a2817e 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -3,20 +3,17 @@ import { Tooltip } from '@material-ui/core';
import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
-import { Copy, Id } from '../../../../fields/FieldSymbols';
-import { InkField } from '../../../../fields/InkField';
+import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
-import { RichTextField } from '../../../../fields/RichTextField';
import { Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
import { Docs, DocUtils } from '../../../documents/Documents';
-import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
+import { CollectionViewType } from '../../../documents/DocumentTypes';
import { DocumentManager } from '../../../util/DocumentManager';
import { DragManager } from '../../../util/DragManager';
import { SettingsManager } from '../../../util/SettingsManager';
import { Transform } from '../../../util/Transform';
import { undoBatch } from '../../../util/UndoManager';
-import { MarqueeView } from '../../collections/collectionFreeForm';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { EditableView } from '../../EditableView';
import { Colors } from '../../global/globalEnums';
@@ -139,7 +136,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
onClick={e => {
e.stopPropagation();
e.preventDefault();
- this.presBoxView?.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
+ this.presBoxView?.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, e.shiftKey || e.ctrlKey || e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
this.presExpandDocumentClick();
}}>
<div className="presItem-groupNum">{`${ind + 1}.`}</div>
@@ -177,22 +174,14 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
const element = e.target as any;
e.stopPropagation();
e.preventDefault();
- if (element && !(e.ctrlKey || e.metaKey)) {
- if (this.selectedArray?.has(this.rootDoc)) {
- this.selectedArray.size === 1 && this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
- setupMoveUpEvents(this, e, this.startDrag, emptyFunction, emptyFunction);
- } else {
- setupMoveUpEvents(
- this,
- e,
- (e: PointerEvent) => {
- this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
- return this.startDrag(e);
- },
- emptyFunction,
- emptyFunction
- );
- }
+ if (element && !(e.ctrlKey || e.metaKey || e.button === 2)) {
+ this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true, false);
+ setupMoveUpEvents(this, e, this.startDrag, emptyFunction, e => {
+ e.stopPropagation();
+ e.preventDefault();
+ this.presBoxView?.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, e.shiftKey || e.ctrlKey || e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
+ this.presBoxView?.activeItem && this.showRecording(this.presBoxView?.activeItem);
+ });
}
};
@@ -304,60 +293,26 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
*/
@undoBatch
@action
- updateCapturedContainerLayout = (targetDoc: Doc, activeItem: Doc) => {
+ updateCapturedContainerLayout = (presTargetDoc: Doc, activeItem: Doc) => {
+ const targetDoc = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc;
activeItem.presX = NumCast(targetDoc.x);
activeItem.presY = NumCast(targetDoc.y);
- activeItem.presRot = NumCast(targetDoc.rotation);
+ activeItem.presRotation = NumCast(targetDoc.rotation);
activeItem.presWidth = NumCast(targetDoc.width);
activeItem.presHeight = NumCast(targetDoc.height);
+ activeItem.presPinLayout = true;
};
/**
* Method called for updating the view of the currently selected document
*
- * @param targetDoc
+ * @param presTargetDoc
* @param activeItem
*/
@undoBatch
@action
- updateCapturedViewContents = (targetDoc: Doc, activeItem: Doc) => {
- switch (targetDoc.type) {
- case DocumentType.PDF:
- case DocumentType.WEB:
- case DocumentType.RTF:
- const scroll = targetDoc._scrollTop;
- activeItem.presPinViewScroll = scroll;
- if (targetDoc.type === DocumentType.RTF) {
- activeItem.presData = targetDoc[Doc.LayoutFieldKey(targetDoc)] instanceof RichTextField ? (targetDoc[Doc.LayoutFieldKey(targetDoc)] as RichTextField)[Copy]() : targetDoc.text;
- }
- break;
- case DocumentType.INK:
- activeItem.presData = targetDoc[Doc.LayoutFieldKey(targetDoc)] instanceof InkField ? (targetDoc[Doc.LayoutFieldKey(targetDoc)] as InkField)[Copy]() : targetDoc.data;
- break;
- case DocumentType.VID:
- case DocumentType.AUDIO:
- activeItem.presStartTime = targetDoc._currentTimecode;
- break;
- case DocumentType.COMPARISON:
- const clipWidth = targetDoc._clipWidth;
- activeItem.presPinClipWidth = clipWidth;
- break;
- case DocumentType.COL:
- activeItem.presPinLayoutData = new List<string>(DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]).map(d => JSON.stringify({ id: d[Id], x: NumCast(d.x), y: NumCast(d.y), w: NumCast(d._width), h: NumCast(d._height) })));
- default:
- const bestView = DocumentManager.Instance.getFirstDocumentView(targetDoc);
- if (activeItem.presPinViewBounds && bestView) {
- const bounds = MarqueeView.CurViewBounds(targetDoc, bestView.props.PanelWidth(), bestView.props.PanelHeight());
- activeItem.presPinView = true;
- activeItem.presPinViewScale = NumCast(targetDoc._viewScale, 1);
- activeItem.presPinViewX = bounds.left + bounds.width / 2;
- activeItem.presPinViewY = bounds.top + bounds.height / 2;
- activeItem.presPinViewBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
- } else {
- activeItem.presPinViewX = targetDoc._panX;
- activeItem.presPinViewY = targetDoc._panY;
- activeItem.presPinViewScale = targetDoc._viewScale;
- }
- }
+ updateCapturedViewContents = (presTargetDoc: Doc, activeItem: Doc) => {
+ const target = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc;
+ PresBox.pinDocView(activeItem, { pinDocContent: true, pinData: PresBox.pinDataTypes(target) }, target);
};
@computed get recordingIsInOverlay() {
@@ -465,24 +420,26 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
const activeItem: Doc = this.rootDoc;
const items: JSX.Element[] = [];
- if (activeItem.presPinLayout) {
- items.push(
- <Tooltip key="slide" title={<div className="dash-tooltip">Update captured doc layout</div>}>
- <div className="slideButton" onClick={() => this.updateCapturedContainerLayout(targetDoc, activeItem)} style={{ fontWeight: 700, display: 'flex' }}>
- L
- </div>
- </Tooltip>
- );
- }
- if (activeItem.presPinData || activeItem.presPinView) {
- items.push(
- <Tooltip key="flex" title={<div className="dash-tooltip">Update captured doc content</div>}>
- <div className="slideButton" onClick={() => this.updateCapturedViewContents(targetDoc, activeItem)} style={{ fontWeight: 700, display: 'flex' }}>
- C
- </div>
- </Tooltip>
- );
- }
+ items.push(
+ <Tooltip key="slide" title={<div className="dash-tooltip">Update captured doc layout</div>}>
+ <div
+ className="slideButton"
+ onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedContainerLayout(targetDoc, activeItem), true)}
+ style={{ opacity: activeItem.presPinLayout ? 1 : 0.5, fontWeight: 700, display: 'flex' }}>
+ L
+ </div>
+ </Tooltip>
+ );
+ items.push(
+ <Tooltip key="flex" title={<div className="dash-tooltip">Update captured doc content</div>}>
+ <div
+ className="slideButton"
+ onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedViewContents(targetDoc, activeItem))}
+ style={{ opacity: activeItem.presPinData || activeItem.presPinView ? 1 : 0.5, fontWeight: 700, display: 'flex' }}>
+ C
+ </div>
+ </Tooltip>
+ );
if (!Doc.noviceMode) {
items.push(
<Tooltip key="slash" title={<div className="dash-tooltip">{this.recordingIsInOverlay ? 'Hide Recording' : `${PresElementBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}</div>}>
@@ -557,15 +514,9 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
paddingTop: NumCast(this.layoutDoc._yPadding, this.props.yPadding),
paddingBottom: NumCast(this.layoutDoc._yPadding, this.props.yPadding),
}}
- onClick={e => {
- e.stopPropagation();
- e.preventDefault();
- this.presBoxView?.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
- this.showRecording(activeItem);
- }}
onDoubleClick={action(e => {
this.toggleProperties();
- this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true);
+ this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false);
})}
onPointerOver={this.onPointerOver}
onPointerLeave={this.onPointerLeave}
@@ -592,7 +543,17 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
width: `calc(100% ${this.rootDoc.presExpandInlineButton ? '- 50%' : ''} - ${this.presButtons.length * 22}px`,
cursor: isSelected ? 'text' : 'grab',
}}>
- <div>{`${this.indexInPres + 1}. `}</div>
+ <div
+ className="presItem-number"
+ title="select without navigation"
+ style={{ pointerEvents: this.presBoxView?.isContentActive() ? 'all' : undefined }}
+ onPointerDown={e => {
+ e.stopPropagation();
+ if (this._itemRef.current && this._dragRef.current) {
+ this.presBoxView?.modifierSelect(activeItem, this._itemRef.current, this._dragRef.current, true, false, false);
+ }
+ }}
+ onClick={e => e.stopPropagation()}>{`${this.indexInPres + 1}. `}</div>
<EditableView ref={this._titleRef} editing={!isSelected ? false : undefined} contents={activeItem.title} overflow={'ellipsis'} GetValue={() => StrCast(activeItem.title)} SetValue={this.onSetValue} />
</div>
{/* <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip> */}