aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authormehekj <mehek.jethani@gmail.com>2023-01-28 22:39:23 -0500
committermehekj <mehek.jethani@gmail.com>2023-01-28 22:39:23 -0500
commit9f139c7f0f571bdfea8ce99fc0a507724eb8fd74 (patch)
tree4de6642112f9c6c2df1a85cf8fa9ed700bb94cbd /src
parent9d2af1180f0dd5af5ab86b922cd8b0cdfcf4ea09 (diff)
parent95b8a5a2b470d3118b6eeac484a45b23df2830b4 (diff)
Merge branch 'master' into schema-mehek
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts16
-rw-r--r--src/client/util/CaptureManager.tsx20
-rw-r--r--src/client/util/CurrentUserUtils.ts5
-rw-r--r--src/client/util/DocumentManager.ts31
-rw-r--r--src/client/util/DragManager.ts4
-rw-r--r--src/client/util/LinkFollower.ts30
-rw-r--r--src/client/util/LinkManager.ts61
-rw-r--r--src/client/util/SharingManager.tsx10
-rw-r--r--src/client/views/AntimodeMenu.tsx143
-rw-r--r--src/client/views/DocComponent.tsx7
-rw-r--r--src/client/views/DocumentButtonBar.tsx32
-rw-r--r--src/client/views/InkingStroke.tsx4
-rw-r--r--src/client/views/LightboxView.tsx57
-rw-r--r--src/client/views/MainView.tsx21
-rw-r--r--src/client/views/MarqueeAnnotator.tsx8
-rw-r--r--src/client/views/OverlayView.tsx136
-rw-r--r--src/client/views/PropertiesView.tsx22
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx9
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx42
-rw-r--r--src/client/views/collections/CollectionSubView.tsx2
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx14
-rw-r--r--src/client/views/collections/TabDocView.tsx5
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx8
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx12
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx60
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx2
-rw-r--r--src/client/views/linking/LinkMenuItem.tsx15
-rw-r--r--src/client/views/nodes/AudioBox.tsx7
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx6
-rw-r--r--src/client/views/nodes/DocumentView.tsx31
-rw-r--r--src/client/views/nodes/FilterBox.tsx2
-rw-r--r--src/client/views/nodes/FunctionPlotBox.tsx7
-rw-r--r--src/client/views/nodes/ImageBox.tsx8
-rw-r--r--src/client/views/nodes/LabelBox.tsx4
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx73
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx5
-rw-r--r--src/client/views/nodes/PDFBox.tsx9
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx4
-rw-r--r--src/client/views/nodes/VideoBox.tsx11
-rw-r--r--src/client/views/nodes/WebBox.tsx6
-rw-r--r--src/client/views/nodes/button/FontIconBox.tsx2
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx6
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx50
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts2
-rw-r--r--src/client/views/nodes/formattedText/marks_rts.ts2
-rw-r--r--src/client/views/nodes/trails/PresBox.scss26
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx543
-rw-r--r--src/client/views/nodes/trails/PresElementBox.tsx22
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx154
-rw-r--r--src/client/views/pdf/Annotation.tsx27
-rw-r--r--src/client/views/pdf/PDFViewer.tsx4
-rw-r--r--src/client/views/search/SearchBox.tsx40
-rw-r--r--src/fields/Doc.ts21
-rw-r--r--src/fields/FieldLoader.scss9
-rw-r--r--src/fields/FieldLoader.tsx14
-rw-r--r--src/fields/Types.ts90
56 files changed, 1064 insertions, 897 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 58e318879..f6104d655 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -841,13 +841,11 @@ export namespace Docs {
viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
}
Doc.assign(viewDoc, viewProps, true, true);
- ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc);
+ if (![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any)) {
+ DocUtils.MakeLinkToActiveAudio(() => viewDoc);
+ }
- !Doc.IsSystem(dataDoc) &&
- ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(proto.type as any) &&
- !dataDoc.isFolder &&
- !dataProps.annotationOn &&
- Doc.AddDocToList(Doc.MyFileOrphans, undefined, dataDoc);
+ Doc.AddFileOrphan(dataDoc);
updateCachedAcls(dataDoc);
updateCachedAcls(viewDoc);
@@ -1323,13 +1321,13 @@ export namespace DocUtils {
options?.afterFocus?.(false);
}
- export let ActiveRecordings: { props: FieldViewProps; getAnchor: () => Doc }[] = [];
+ export let ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = [];
export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) {
broadcastEvent && runInAction(() => (DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1));
return DocUtils.ActiveRecordings.map(audio => {
const sourceDoc = getSourceDoc();
- const link = sourceDoc && DocUtils.MakeLink({ doc: sourceDoc }, { doc: audio.getAnchor() || audio.props.Document }, 'recording annotation:linked recording', 'recording timeline');
+ const link = sourceDoc && DocUtils.MakeLink({ doc: sourceDoc }, { doc: audio.getAnchor(true) || audio.props.Document }, 'recording annotation:linked recording', 'recording timeline');
link && (link.followLinkLocation = OpenWhere.addRight);
return link;
});
@@ -1886,7 +1884,7 @@ export namespace DocUtils {
export function copyDragFactory(dragFactory: Doc) {
if (!dragFactory) return undefined;
const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true);
- ndoc && Doc.AddDocToList(Doc.MyFileOrphans, 'data', Doc.GetProto(ndoc));
+ ndoc && Doc.AddFileOrphan(Doc.GetProto(ndoc));
if (ndoc && dragFactory['dragFactory-count'] !== undefined) {
dragFactory['dragFactory-count'] = NumCast(dragFactory['dragFactory-count']) + 1;
Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory['dragFactory-count']).toString(), true);
diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx
index 0b5957fac..735b06f6d 100644
--- a/src/client/util/CaptureManager.tsx
+++ b/src/client/util/CaptureManager.tsx
@@ -1,19 +1,14 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, runInAction } from 'mobx';
+import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { convertToObject } from 'typescript';
import { Doc, DocListCast } from '../../fields/Doc';
-import { BoolCast, StrCast, Cast } from '../../fields/Types';
-import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils';
+import { StrCast } from '../../fields/Types';
+import { addStyleSheet } from '../../Utils';
import { LightboxView } from '../views/LightboxView';
import { MainViewModal } from '../views/MainViewModal';
import './CaptureManager.scss';
import { SelectionManager } from './SelectionManager';
-import { undoBatch } from './UndoManager';
-const higflyout = require('@hig/flyout');
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
@observer
export class CaptureManager extends React.Component<{}> {
@@ -53,15 +48,8 @@ export class CaptureManager extends React.Component<{}> {
const doc = this._document;
const order: JSX.Element[] = [];
if (doc) {
- console.log('title', doc.title);
- console.log('links', doc.links);
- const linkDocs = DocListCast(doc.links);
- const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc)); // link docs where 'doc' is anchor1
- const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, doc)); // link docs where 'doc' is anchor2
- linkDocs.forEach((l, i) => {
+ DocListCast(doc.links).forEach((l, i) => {
if (l) {
- console.log(i, (l.anchor1 as Doc).title);
- console.log(i, (l.anchor2 as Doc).title);
order.push(
<div className="list-item">
<div className="number">{i}</div>
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index f2fb0afda..5f183cf91 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -754,12 +754,13 @@ export class CurrentUserUtils {
const sharedDocOpts:DocumentOptions = {
title: "My Shared Docs",
userColor: "rgb(202, 202, 202)",
+ isFolder:true,
childContextMenuFilters: new List<ScriptField>([dashboardFilter!,]),
childContextMenuScripts: new List<ScriptField>([addToDashboards!,]),
childContextMenuLabels: new List<string>(["Add to Dashboards",]),
childContextMenuIcons: new List<string>(["user-plus",]),
"acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
- childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15,
+ childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
// NOTE: treeViewHideTitle & _showTitle is for a TreeView's editable title, _showTitle is for DocumentViews title bar
_showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true,
explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
@@ -831,6 +832,8 @@ export class CurrentUserUtils {
DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) => Docs.Prototypes.MainScriptDocument(), {});
DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents
+ Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs)
+
if (doc.activeDashboard instanceof Doc) {
// undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
doc.activeDashboard.colorScheme = doc.activeDashboard.colorScheme === ColorScheme.Light ? undefined : doc.activeDashboard.colorScheme;
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 70fe7f2c0..7c867d710 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -5,7 +5,7 @@ import { listSpec } from '../../fields/Schema';
import { Cast, DocCast, StrCast } from '../../fields/Types';
import { AudioField } from '../../fields/URLField';
import { returnFalse } from '../../Utils';
-import { DocumentType } from '../documents/DocumentTypes';
+import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
import { CollectionDockingView } from '../views/collections/CollectionDockingView';
import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
import { CollectionView } from '../views/collections/CollectionView';
@@ -32,11 +32,15 @@ export class DocumentManager {
private constructor() {}
private _viewRenderedCbs: { doc: Doc; func: (dv: DocumentView) => any }[] = [];
- public AddViewRenderedCb = (doc: Doc, func: (dv: DocumentView) => any) => {
- const dv = this.getDocumentViewById(doc[Id]);
- this._viewRenderedCbs.push({ doc, func });
- if (dv) {
- this.callAddViewFuncs(dv);
+ public AddViewRenderedCb = (doc: Opt<Doc>, func: (dv: DocumentView) => any) => {
+ if (doc) {
+ const dv = this.getDocumentViewById(doc[Id]);
+ this._viewRenderedCbs.push({ doc, func });
+ if (dv) {
+ this.callAddViewFuncs(dv);
+ }
+ } else {
+ func(undefined as any);
}
};
callAddViewFuncs = (view: DocumentView) => {
@@ -190,6 +194,21 @@ export class DocumentManager {
return toReturn;
}
+ static GetContextPath(doc: Opt<Doc>, includeExistingViews?: boolean) {
+ if (!doc) return [];
+ const srcContext = Cast(doc.context, Doc, null) ?? Cast(Cast(doc.annotationOn, Doc, null)?.context, Doc, null);
+ var containerDocContext = srcContext ? [srcContext] : [];
+ while (
+ containerDocContext.length &&
+ containerDocContext[0]?.context &&
+ DocCast(containerDocContext[0].context)?.viewType !== CollectionViewType.Docking &&
+ (includeExistingViews || !DocumentManager.Instance.getDocumentView(containerDocContext[0]))
+ ) {
+ containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
+ }
+ return containerDocContext;
+ }
+
static playAudioAnno(doc: Doc) {
const anno = Cast(doc[Doc.LayoutFieldKey(doc) + '-audioAnnotations'], listSpec(AudioField), null)?.lastElement();
if (anno) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index d0690fa10..a56f87075 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -251,8 +251,8 @@ export namespace DragManager {
}
// drags a linker button and creates a link on drop
- export function StartLinkDrag(ele: HTMLElement, sourceView: DocumentView, sourceDocGetAnchor: undefined | (() => Doc), downX: number, downY: number, options?: DragOptions) {
- StartDrag([ele], new DragManager.LinkDragData(sourceView, () => sourceDocGetAnchor?.() ?? sourceView.rootDoc), downX, downY, options);
+ export function StartLinkDrag(ele: HTMLElement, sourceView: DocumentView, sourceDocGetAnchor: undefined | ((addAsAnnotation: boolean) => Doc), downX: number, downY: number, options?: DragOptions) {
+ StartDrag([ele], new DragManager.LinkDragData(sourceView, () => sourceDocGetAnchor?.(true) ?? sourceView.rootDoc), downX, downY, options);
}
// drags a column from a schema view
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index d5ef9fab6..57618b53c 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -1,12 +1,15 @@
import { action, runInAction } from 'mobx';
import { Doc, DocListCast, Opt } from '../../fields/Doc';
import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
-import { DocumentType } from '../documents/DocumentTypes';
+import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
+import { CollectionDockingView } from '../views/collections/CollectionDockingView';
import { DocumentDecorations } from '../views/DocumentDecorations';
import { LightboxView } from '../views/LightboxView';
-import { DocFocusOptions, DocumentViewSharedProps, OpenWhere, ViewAdjustment } from '../views/nodes/DocumentView';
+import { DocFocusOptions, DocumentViewSharedProps, OpenWhere, OpenWhereMod, ViewAdjustment } from '../views/nodes/DocumentView';
+import { PresBox } from '../views/nodes/trails';
import { DocumentManager } from './DocumentManager';
import { LinkManager } from './LinkManager';
+import { SelectionManager } from './SelectionManager';
import { UndoManager } from './UndoManager';
type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void;
@@ -108,22 +111,17 @@ export class LinkFollower {
easeFunc: StrCast(sourceDoc.followLinkEase, 'ease') as any,
effect: sourceDoc,
originatingDoc: sourceDoc,
- zoomTextSelections: false,
+ zoomTextSelections: BoolCast(sourceDoc.followLinkZoomText),
};
- if (target.TourMap) {
- const fieldKey = Doc.LayoutFieldKey(target);
- const tour = DocListCast(target[fieldKey]).reverse();
- LightboxView.SetLightboxDoc(currentContext, undefined, tour);
- setTimeout(LightboxView.Next);
- allFinished();
+ if (target.type === DocumentType.PRES) {
+ const containerDocContext = DocumentManager.GetContextPath(sourceDoc, true); // gather all views that affect layout of sourceDoc so we can revert them after playing the rail
+ SelectionManager.DeselectAll();
+ DocumentManager.Instance.AddViewRenderedCb(target, dv => containerDocContext.length && (dv.ComponentView as PresBox).PlayTrail(containerDocContext));
+ PresBox.OpenPresMinimized(target, [0, 0]);
+ finished?.();
} else {
- const containerAnnoDoc = Cast(target.annotationOn, Doc, null);
- const containerDoc = containerAnnoDoc || target;
- var containerDocContext = containerDoc?.context ? [Cast(containerDoc?.context, Doc, null)] : ([] as Doc[]);
- while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) {
- containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
- }
- const targetContexts = LightboxView.LightboxDoc ? [containerAnnoDoc || containerDocContext[0]].filter(a => a) : containerDocContext;
+ const containerDocContext = DocumentManager.GetContextPath(target);
+ const targetContexts = !sourceDoc.followLinkToOuterContext && containerDocContext.length ? [containerDocContext.lastElement()] : containerDocContext;
DocumentManager.Instance.jumpToDocument(target, options, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, OpenWhere.inPlace), finished), targetContexts, allFinished);
}
};
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 2b0ce1d3d..7da16ca78 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -3,7 +3,7 @@ import { computedFn } from 'mobx-utils';
import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
import { List } from '../../fields/List';
import { ProxyField } from '../../fields/Proxy';
-import { Cast, StrCast } from '../../fields/Types';
+import { Cast, DocCast, PromiseValue, StrCast } from '../../fields/Types';
/*
* link doc:
* - anchor1: doc
@@ -24,7 +24,15 @@ export class LinkManager {
public static get Instance() {
return LinkManager._instance;
}
- public static addLinkDB = (linkDb: any) => LinkManager.userLinkDBs.push(linkDb);
+ public static addLinkDB = async (linkDb: any) => {
+ await Promise.all(
+ ((await DocListCastAsync(linkDb.data)) ?? []).map(link =>
+ // makes sure link anchors are loaded to avoid incremental updates to computedFns in LinkManager
+ [PromiseValue(link?.anchor1), PromiseValue(link?.anchor2)]
+ )
+ );
+ LinkManager.userLinkDBs.push(linkDb);
+ };
public static AutoKeywords = 'keywords:Usages';
static links: Doc[] = [];
constructor() {
@@ -32,21 +40,22 @@ export class LinkManager {
this.createLinkrelationshipLists();
LinkManager.userLinkDBs = [];
const addLinkToDoc = (link: Doc) => {
- const a1Prom = link?.anchor1;
- const a2Prom = link?.anchor2;
- Promise.all([a1Prom, a2Prom]).then(all => {
- const a1 = all[0];
- const a2 = all[1];
- const a1ProtoProm = (link?.anchor1 as Doc)?.proto;
- const a2ProtoProm = (link?.anchor2 as Doc)?.proto;
- Promise.all([a1ProtoProm, a2ProtoProm]).then(
- action(all => {
- if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
- Doc.GetProto(a1)[DirectLinksSym].add(link);
- Doc.GetProto(a2)[DirectLinksSym].add(link);
- }
- })
- );
+ Promise.all([link]).then(linkdoc => {
+ const link = DocCast(linkdoc[0]);
+ Promise.all([link.proto]).then(linkproto => {
+ Promise.all([link.anchor1, link.anchor2]).then(linkdata => {
+ const a1 = DocCast(linkdata[0]);
+ const a2 = DocCast(linkdata[1]);
+ a1 &&
+ a2 &&
+ Promise.all([a1.proto, a2.proto]).then(
+ action(protos => {
+ (protos[0] as Doc)?.[DirectLinksSym].add(link);
+ (protos[1] as Doc)?.[DirectLinksSym].add(link);
+ })
+ );
+ });
+ });
});
};
const remLinkFromDoc = (link: Doc) => {
@@ -114,13 +123,6 @@ export class LinkManager {
true
);
LinkManager.addLinkDB(Doc.LinkDBDoc());
- DocListCastAsync(Doc.LinkDBDoc()?.data).then(dblist =>
- dblist?.forEach(async link => {
- // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
- const a1 = await Cast(link?.anchor1, Doc, null);
- const a2 = await Cast(link?.anchor2, Doc, null);
- })
- );
}
public createLinkrelationshipLists = () => {
@@ -150,19 +152,14 @@ export class LinkManager {
} // finds all links that contain the given anchor
relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc): Doc[] {
- const lfield = Doc.LayoutFieldKey(anchor);
if (!anchor || anchor instanceof Promise || Doc.GetProto(anchor) instanceof Promise) {
console.log('WAITING FOR DOC/PROTO IN LINKMANAGER');
return [];
}
const dirLinks = Doc.GetProto(anchor)[DirectLinksSym];
- const annos = DocListCast(anchor[lfield + '-annotations']);
- const timelineAnnos = DocListCast(anchor[lfield + '-annotations-timeline']);
- if (!annos || !timelineAnnos) {
- debugger;
- }
- const related = [...annos, ...timelineAnnos].reduce((list, anno) => [...list, ...LinkManager.Instance.relatedLinker(anno)], Array.from(dirLinks).slice());
- return related;
+ const annos = DocListCast(anchor[Doc.LayoutFieldKey(anchor) + '-annotations']);
+ if (!annos) debugger;
+ return annos.reduce((list, anno) => [...list, ...LinkManager.Instance.relatedLinker(anno)], Array.from(dirLinks).slice());
}, true);
// returns map of group type to anchor's links in that group type
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 824a862cb..00ae85d12 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -8,7 +8,7 @@ import * as RequestPromise from 'request-promise';
import { AclAdmin, AclPrivate, AclSym, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, HierarchyMapping, Opt } from '../../fields/Doc';
import { Id } from '../../fields/FieldSymbols';
import { List } from '../../fields/List';
-import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { Cast, NumCast, PromiseValue, StrCast } from '../../fields/Types';
import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from '../../fields/util';
import { Utils } from '../../Utils';
import { DocServer } from '../DocServer';
@@ -138,12 +138,6 @@ export class SharingManager extends React.Component<{}> {
const sharingDoc = await DocServer.GetRefField(user.sharingDocumentId);
const linkDatabase = await DocServer.GetRefField(user.linkDatabaseId);
if (sharingDoc instanceof Doc && linkDatabase instanceof Doc) {
- await DocListCastAsync(linkDatabase.data);
- (await DocListCastAsync(Cast(linkDatabase, Doc, null).data))?.forEach(async link => {
- // makes sure link anchors are loaded to avoid incremental updates to computedFns in LinkManager
- const a1 = await Cast(link?.anchor1, Doc, null);
- const a2 = await Cast(link?.anchor2, Doc, null);
- });
sharingDocs.push({ user, sharingDoc, linkDatabase, userColor: StrCast(sharingDoc.userColor) });
}
}
@@ -153,7 +147,7 @@ export class SharingManager extends React.Component<{}> {
for (const sharer of sharingDocs) {
if (!this.users.find(user => user.user.email === sharer.user.email)) {
this.users.push(sharer);
- //LinkManager.addLinkDB(sharer.linkDatabase);
+ // LinkManager.addLinkDB(sharer.linkDatabase);
}
}
});
diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx
index 0f1fc6b69..de1207ce4 100644
--- a/src/client/views/AntimodeMenu.tsx
+++ b/src/client/views/AntimodeMenu.tsx
@@ -1,8 +1,7 @@
-import React = require("react");
-import { observable, action, runInAction } from "mobx";
-import "./AntimodeMenu.scss";
-export interface AntimodeMenuProps {
-}
+import React = require('react');
+import { observable, action, runInAction } from 'mobx';
+import './AntimodeMenu.scss';
+export interface AntimodeMenuProps {}
/**
* This is an abstract class that serves as the base for a PDF-style or Marquee-style
@@ -17,15 +16,19 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends React.Co
@observable protected _top: number = -300;
@observable protected _left: number = -300;
@observable protected _opacity: number = 0;
- @observable protected _transitionProperty: string = "opacity";
- @observable protected _transitionDuration: string = "0.5s";
- @observable protected _transitionDelay: string = "";
+ @observable protected _transitionProperty: string = 'opacity';
+ @observable protected _transitionDuration: string = '0.5s';
+ @observable protected _transitionDelay: string = '';
@observable protected _canFade: boolean = false;
@observable public Pinned: boolean = false;
- get width() { return this._mainCont.current ? this._mainCont.current.getBoundingClientRect().width : 0; }
- get height() { return this._mainCont.current ? this._mainCont.current.getBoundingClientRect().height : 0; }
+ get width() {
+ return this._mainCont.current ? this._mainCont.current.getBoundingClientRect().width : 0;
+ }
+ get height() {
+ return this._mainCont.current ? this._mainCont.current.getBoundingClientRect().height : 0;
+ }
@action
/**
@@ -36,12 +39,12 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends React.Co
*/
public jumpTo = (x: number, y: number, forceJump: boolean = false) => {
if (!this.Pinned || forceJump) {
- this._transitionProperty = this._transitionDuration = this._transitionDelay = "";
+ this._transitionProperty = this._transitionDuration = this._transitionDelay = '';
this._opacity = 1;
this._left = x;
this._top = y;
}
- }
+ };
@action
/**
@@ -51,56 +54,56 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends React.Co
public fadeOut = (forceOut: boolean) => {
if (!this.Pinned) {
if (this._opacity === 0.2) {
- this._transitionProperty = "opacity";
- this._transitionDuration = "0.1s";
+ this._transitionProperty = 'opacity';
+ this._transitionDuration = '0.1s';
}
if (forceOut) {
- this._transitionProperty = "";
- this._transitionDuration = "";
+ this._transitionProperty = '';
+ this._transitionDuration = '';
}
- this._transitionDelay = "";
+ this._transitionDelay = '';
this._opacity = 0;
this._left = this._top = -300;
}
- }
+ };
@action
protected pointerLeave = (e: React.PointerEvent) => {
if (!this.Pinned && this._canFade) {
- this._transitionProperty = "opacity";
- this._transitionDuration = "0.5s";
- this._transitionDelay = "1s";
+ this._transitionProperty = 'opacity';
+ this._transitionDuration = '0.5s';
+ this._transitionDelay = '1s';
this._opacity = 0.2;
setTimeout(() => this.fadeOut(false), 3000);
}
- }
+ };
@action
protected pointerEntered = (e: React.PointerEvent) => {
- this._transitionProperty = "opacity";
- this._transitionDuration = "0.1s";
- this._transitionDelay = "";
+ this._transitionProperty = 'opacity';
+ this._transitionDuration = '0.1s';
+ this._transitionDelay = '';
this._opacity = 1;
- }
+ };
@action
protected togglePin = (e: React.MouseEvent) => {
- runInAction(() => this.Pinned = !this.Pinned);
- }
+ runInAction(() => (this.Pinned = !this.Pinned));
+ };
protected dragStart = (e: React.PointerEvent) => {
- document.removeEventListener("pointermove", this.dragging);
- document.addEventListener("pointermove", this.dragging);
- document.removeEventListener("pointerup", this.dragEnd);
- document.addEventListener("pointerup", this.dragEnd);
+ document.removeEventListener('pointermove', this.dragging);
+ document.addEventListener('pointermove', this.dragging);
+ document.removeEventListener('pointerup', this.dragEnd);
+ document.addEventListener('pointerup', this.dragEnd);
this._offsetX = e.pageX - this._mainCont.current!.getBoundingClientRect().left;
this._offsetY = e.pageY - this._mainCont.current!.getBoundingClientRect().top;
e.stopPropagation();
e.preventDefault();
- }
+ };
@action
protected dragging = (e: PointerEvent) => {
@@ -115,32 +118,41 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends React.Co
e.stopPropagation();
e.preventDefault();
- }
+ };
protected dragEnd = (e: PointerEvent) => {
- document.removeEventListener("pointermove", this.dragging);
- document.removeEventListener("pointerup", this.dragEnd);
+ document.removeEventListener('pointermove', this.dragging);
+ document.removeEventListener('pointerup', this.dragEnd);
e.stopPropagation();
e.preventDefault();
- }
+ };
protected handleContextMenu = (e: React.MouseEvent) => {
e.stopPropagation();
e.preventDefault();
- }
+ };
protected getDragger = () => {
- return <div className="antimodeMenu-dragger" key="dragger" onPointerDown={this.dragStart} style={{ width: "20px" }} />;
- }
+ return <div className="antimodeMenu-dragger" key="dragger" onPointerDown={this.dragStart} style={{ width: '20px' }} />;
+ };
- protected getElement(buttons: JSX.Element[]) {
+ protected getElement(buttons: JSX.Element) {
return (
- <div className="antimodeMenu-cont" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
+ <div
+ className="antimodeMenu-cont"
+ onPointerLeave={this.pointerLeave}
+ onPointerEnter={this.pointerEntered}
+ ref={this._mainCont}
+ onContextMenu={this.handleContextMenu}
style={{
- left: this._left, top: this._top, opacity: this._opacity, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay,
- position: this.Pinned ? "unset" : undefined
+ left: this._left,
+ top: this._top,
+ opacity: this._opacity,
+ transitionProperty: this._transitionProperty,
+ transitionDuration: this._transitionDuration,
+ transitionDelay: this._transitionDelay,
+ position: this.Pinned ? 'unset' : undefined,
}}>
- {/* {this.getDragger} */}
{buttons}
</div>
);
@@ -148,34 +160,51 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends React.Co
protected getElementVert(buttons: JSX.Element[]) {
return (
- <div className="antimodeMenu-cont" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
+ <div
+ className="antimodeMenu-cont"
+ onPointerLeave={this.pointerLeave}
+ onPointerEnter={this.pointerEntered}
+ ref={this._mainCont}
+ onContextMenu={this.handleContextMenu}
style={{
left: this.Pinned ? undefined : this._left,
top: this.Pinned ? 0 : this._top,
right: this.Pinned ? 0 : undefined,
- height: "inherit",
+ height: 'inherit',
width: 200,
- opacity: this._opacity, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay,
- position: this.Pinned ? "absolute" : undefined
+ opacity: this._opacity,
+ transitionProperty: this._transitionProperty,
+ transitionDuration: this._transitionDuration,
+ transitionDelay: this._transitionDelay,
+ position: this.Pinned ? 'absolute' : undefined,
}}>
{buttons}
</div>
);
}
-
-
protected getElementWithRows(rows: JSX.Element[], numRows: number, hasDragger: boolean = true) {
return (
- <div className="antimodeMenu-cont with-rows" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
+ <div
+ className="antimodeMenu-cont with-rows"
+ onPointerLeave={this.pointerLeave}
+ onPointerEnter={this.pointerEntered}
+ ref={this._mainCont}
+ onContextMenu={this.handleContextMenu}
style={{
- left: this._left, top: this._top, opacity: this._opacity, transitionProperty: this._transitionProperty,
- transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay, height: "auto",
- flexDirection: this.Pinned ? "row" : undefined, position: this.Pinned ? "unset" : undefined
+ left: this._left,
+ top: this._top,
+ opacity: this._opacity,
+ transitionProperty: this._transitionProperty,
+ transitionDuration: this._transitionDuration,
+ transitionDelay: this._transitionDelay,
+ height: 'auto',
+ flexDirection: this.Pinned ? 'row' : undefined,
+ position: this.Pinned ? 'unset' : undefined,
}}>
- {hasDragger ? <div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: "20px" }} /> : (null)}
+ {hasDragger ? <div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: '20px' }} /> : null}
{rows}
</div>
);
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index c9c09b63b..78ab2b3d4 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -130,6 +130,13 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
isAnyChildContentActive = () => this._isAnyChildContentActive;
+ isContentActive = (outsideReaction?: boolean) =>
+ this.props.isContentActive?.() === false
+ ? false
+ : Doc.ActiveTool !== InkTool.None || this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this.props.rootSelected(outsideReaction) || this.isAnyChildContentActive()
+ ? true
+ : undefined;
+
lookupField = (field: string) => ScriptCast((this.layoutDoc as any).lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field }).result;
protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 90c6c040c..f61d147cf 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -5,11 +5,11 @@ import { action, computed, observable, runInAction, trace } from 'mobx';
import { observer } from 'mobx-react';
import { Doc } from '../../fields/Doc';
import { RichTextField } from '../../fields/RichTextField';
-import { Cast, NumCast } from '../../fields/Types';
+import { Cast, DocCast, NumCast } from '../../fields/Types';
import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from '../../Utils';
import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
-import { Docs } from '../documents/Documents';
+import { Docs, DocUtils } from '../documents/Documents';
import { DragManager } from '../util/DragManager';
import { SelectionManager } from '../util/SelectionManager';
import { SharingManager } from '../util/SharingManager';
@@ -21,12 +21,13 @@ import { Colors } from './global/globalEnums';
import { LinkPopup } from './linking/LinkPopup';
import { MetadataEntryMenu } from './MetadataEntryMenu';
import { DocumentLinksButton } from './nodes/DocumentLinksButton';
-import { DocumentView, DocumentViewInternal, OpenWhereMod } from './nodes/DocumentView';
+import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from './nodes/DocumentView';
import { DashFieldView } from './nodes/formattedText/DashFieldView';
import { GoogleRef } from './nodes/formattedText/FormattedTextBox';
import { TemplateMenu } from './TemplateMenu';
import React = require('react');
import { DocumentType } from '../documents/DocumentTypes';
+import { FontIconBox } from './nodes/button/FontIconBox';
const higflyout = require('@hig/flyout');
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -270,6 +271,13 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
</button>
</div>
</Tooltip>
+ <Tooltip title={<div>open linked trail</div>}>
+ <div className="documentButtonBar-button">
+ <button style={{ backgroundColor: 'transparent', width: 35, height: 35, display: 'flex', justifyContent: 'center', alignItems: 'center', position: 'relative' }} onPointerDown={this.toggleTrail}>
+ <FontAwesomeIcon icon="taxi" size="lg" />
+ </button>
+ </div>
+ </Tooltip>
</div>
<div style={{ width: 25, height: 25 }}>
<DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={true} />
@@ -483,6 +491,22 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
this._showLinkPopup = !this._showLinkPopup;
e.stopPropagation();
};
+ @action
+ toggleTrail = (e: React.PointerEvent) => {
+ const rootView = this.props.views()[0];
+ const rootDoc = rootView?.rootDoc;
+ if (rootDoc) {
+ const anchor = rootView.ComponentView?.getAnchor?.(true) ?? rootDoc;
+ const trail = DocCast(anchor.presTrail) ?? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail), true);
+ if (trail !== anchor.presTrail) {
+ DocUtils.MakeLink({ doc: anchor }, { doc: trail }, 'link trail');
+ anchor.presTrail = trail;
+ }
+ Doc.ActivePresentation = trail;
+ this.props.views().lastElement()?.props.addDocTab(trail, OpenWhere.replaceRight);
+ }
+ e.stopPropagation();
+ };
render() {
if (!this.view0) return null;
@@ -501,7 +525,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
key="popup"
showPopup={this._showLinkPopup}
linkCreated={link => (link.linkDisplay = !this.props.views().lastElement()?.rootDoc.isLinkButton)}
- linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.()}
+ linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.(true)}
linkFrom={() => this.props.views().lastElement()?.rootDoc}
/>
</div>
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index a6fa2f04b..319a9419e 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -78,8 +78,8 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
// fit within its panel (e.g., for content fitting views like Lightbox or multicolumn, etc)
screenToLocal = () => this.props.ScreenToLocalTransform().scale(this.props.NativeDimScaling?.() || 1);
- getAnchor = () => {
- return this._subContentView?.getAnchor?.() || this.rootDoc;
+ getAnchor = (addAsAnnotation: boolean) => {
+ return this._subContentView?.getAnchor?.(addAsAnnotation) || this.rootDoc;
};
scrollFocus = (textAnchor: Doc, options: DocFocusOptions) => {
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 140be018b..3627aa783 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -37,7 +37,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
@observable private static _doc: Opt<Doc>;
@observable private static _docTarget: Opt<Doc>;
@observable private static _docFilters: string[] = []; // filters
- @observable private static _tourMap: Opt<Doc[]> = []; // list of all tours available from the current target
private static _savedState: Opt<{ panX: Opt<number>; panY: Opt<number>; scale: Opt<number>; scrollTop: Opt<number> }>;
private static _history: Opt<{ doc: Doc; target?: Doc }[]> = [];
@observable private static _future: Opt<Doc[]> = [];
@@ -90,13 +89,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
this._doc = doc;
this._layoutTemplate = layoutTemplate;
this._docTarget = target || doc;
- this._tourMap = DocListCast(doc?.links)
- .map(link => {
- const opp = LinkManager.getOppositeAnchor(link, doc!);
- return opp?.TourMap ? opp : undefined;
- })
- .filter(m => m)
- .map(m => m!);
return true;
}
@@ -164,7 +156,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
const target = (LightboxView._docTarget = this._future?.pop());
const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target);
if (targetDocView && target) {
- const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.() || target).lastElement();
+ const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement();
l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen');
targetDocView.focus(target, { originalTarget: target, willPanZoom: true, zoomScale: 0.9 });
if (LightboxView._history?.lastElement().target !== target) LightboxView._history?.push({ doc, target });
@@ -189,14 +181,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
LightboxView.SetLightboxDoc(target);
}
}
- LightboxView._tourMap = DocListCast(LightboxView._docTarget?.links)
- .map(link => {
- const opp = LinkManager.getOppositeAnchor(link, LightboxView._docTarget!);
- return opp?.TourMap ? opp : undefined;
- })
- .filter(m => m)
- .map(m => m!);
- console.log(LightboxView._tourMap);
}
@action public static Previous() {
@@ -215,13 +199,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
LightboxView.SetLightboxDoc(doc, target);
}
if (LightboxView._future?.lastElement() !== previous.target || previous.doc) LightboxView._future?.push(previous.target || previous.doc);
- LightboxView._tourMap = DocListCast(LightboxView._docTarget?.links)
- .map(link => {
- const opp = LinkManager.getOppositeAnchor(link, LightboxView._docTarget!);
- return opp?.TourMap ? opp : undefined;
- })
- .filter(m => m)
- .map(m => m!);
}
@action
stepInto = () => {
@@ -232,27 +209,20 @@ export class LightboxView extends React.Component<LightboxViewProps> {
history: LightboxView._history,
saved: LightboxView._savedState,
});
- const tours = LightboxView._tourMap;
- if (tours && tours.length) {
- const fieldKey = Doc.LayoutFieldKey(tours[0]);
- LightboxView._future?.push(...DocListCast(tours[0][fieldKey]).reverse());
- } else {
- const coll = LightboxView._docTarget;
- if (coll) {
- const fieldKey = Doc.LayoutFieldKey(coll);
- const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '-annotations'])];
- const links = DocListCast(coll.links)
- .map(link => LinkManager.getOppositeAnchor(link, coll))
- .filter(doc => doc)
- .map(doc => doc!);
- LightboxView.SetLightboxDoc(coll, undefined, contents.length ? contents : links);
- TabDocView.PinDoc(coll, { hidePresBox: true });
- }
+ const coll = LightboxView._docTarget;
+ if (coll) {
+ const fieldKey = Doc.LayoutFieldKey(coll);
+ const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '-annotations'])];
+ const links = DocListCast(coll.links)
+ .map(link => LinkManager.getOppositeAnchor(link, coll))
+ .filter(doc => doc)
+ .map(doc => doc!);
+ LightboxView.SetLightboxDoc(coll, undefined, contents.length ? contents : links);
+ TabDocView.PinDoc(coll, { hidePresBox: true });
}
};
future = () => LightboxView._future;
- tourMap = () => LightboxView._tourMap;
render() {
let downx = 0,
downy = 0;
@@ -346,7 +316,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
},
this.future()?.length.toString()
)}
- <LightboxTourBtn navBtn={this.navBtn} future={this.future} stepInto={this.stepInto} tourMap={this.tourMap} />
+ <LightboxTourBtn navBtn={this.navBtn} future={this.future} stepInto={this.stepInto} />
<div
className="lightboxView-navBtn"
title={'toggle fit width'}
@@ -394,7 +364,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
}
interface LightboxTourBtnProps {
navBtn: (left: Opt<string | number>, bottom: Opt<number>, top: number, icon: string, display: () => string, click: (e: React.MouseEvent) => void, color?: string) => JSX.Element;
- tourMap: () => Opt<Doc[]>;
future: () => Opt<Doc[]>;
stepInto: () => void;
}
@@ -411,7 +380,7 @@ export class LightboxTourBtn extends React.Component<LightboxTourBtnProps> {
e.stopPropagation();
this.props.stepInto();
},
- StrCast(this.props.tourMap()?.lastElement()?.TourMap)
+ ''
);
}
}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 39cc9ba8e..895ed9bda 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -249,6 +249,7 @@ export class MainView extends React.Component {
fa.faTrash,
fa.faTrashAlt,
fa.faShare,
+ fa.faTaxi,
fa.faDownload,
fa.faExpandArrowsAlt,
fa.faLayerGroup,
@@ -574,7 +575,7 @@ export class MainView extends React.Component {
Document={this.headerBarDoc}
DataDoc={undefined}
addDocument={undefined}
- addDocTab={this.addDocTabFunc}
+ addDocTab={MainView.addDocTabFunc}
pinToPres={emptyFunction}
docViewPath={returnEmptyDoclist}
styleProvider={DefaultStyleProvider}
@@ -611,7 +612,7 @@ export class MainView extends React.Component {
Document={this.mainContainer!}
DataDoc={undefined}
addDocument={undefined}
- addDocTab={this.addDocTabFunc}
+ addDocTab={MainView.addDocTabFunc}
pinToPres={emptyFunction}
docViewPath={returnEmptyDoclist}
styleProvider={this._hideUI ? DefaultStyleProvider : undefined}
@@ -682,7 +683,7 @@ export class MainView extends React.Component {
sidebarScreenToLocal = () => new Transform(0, -this.topOfSidebarDoc, 1);
mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0);
- addDocTabFunc = (doc: Doc, location: OpenWhere): boolean => {
+ static addDocTabFunc = (doc: Doc, location: OpenWhere): boolean => {
const whereFields = doc._viewType === CollectionViewType.Docking ? [OpenWhere.dashboard] : location.split(':');
const whereMods = whereFields.length > 1 ? (whereFields[1] as OpenWhereMod) : OpenWhereMod.none;
if (doc.dockingConfig) return DashboardView.openDashboard(doc);
@@ -710,7 +711,7 @@ export class MainView extends React.Component {
Document={this._sidebarContent.proto || this._sidebarContent}
DataDoc={undefined}
addDocument={undefined}
- addDocTab={this.addDocTabFunc}
+ addDocTab={MainView.addDocTabFunc}
pinToPres={emptyFunction}
docViewPath={returnEmptyDoclist}
styleProvider={this._sidebarContent.proto === Doc.MyDashboards || this._sidebarContent.proto === Doc.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
@@ -744,7 +745,7 @@ export class MainView extends React.Component {
Document={Doc.MyLeftSidebarMenu}
DataDoc={undefined}
addDocument={undefined}
- addDocTab={this.addDocTabFunc}
+ addDocTab={MainView.addDocTabFunc}
pinToPres={emptyFunction}
rootSelected={returnTrue}
removeDocument={returnFalse}
@@ -808,7 +809,7 @@ export class MainView extends React.Component {
</div>
)}
<div className="properties-container" style={{ width: this.propertiesWidth() }}>
- {this.propertiesWidth() < 10 ? null : <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={this.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
+ {this.propertiesWidth() < 10 ? null : <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={MainView.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
</div>
</div>
</div>
@@ -888,7 +889,7 @@ export class MainView extends React.Component {
moveDocument={this.moveButtonDoc}
CollectionView={undefined}
addDocument={this.addButtonDoc}
- addDocTab={this.addDocTabFunc}
+ addDocTab={MainView.addDocTabFunc}
pinToPres={emptyFunction}
removeDocument={this.remButtonDoc}
ScreenToLocalTransform={this.buttonBarXf}
@@ -947,6 +948,10 @@ export class MainView extends React.Component {
);
}
+ @computed get linkDocPreview() {
+ return LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : null;
+ }
+
render() {
return (
<div
@@ -974,7 +979,7 @@ export class MainView extends React.Component {
{this._hideUI ? null : <TopBar />}
{LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
{DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => (DocumentLinksButton.LinkEditorDocView = undefined))} docView={DocumentLinksButton.LinkEditorDocView} /> : null}
- {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : null}
+ {this.linkDocPreview}
{((page: string) => {
// prettier-ignore
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index bf1242346..5ab91dd70 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -52,8 +52,8 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true));
AnchorMenu.Instance.OnAudio = unimplementedFunction;
AnchorMenu.Instance.Highlight = this.highlight;
- AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations);
- AnchorMenu.Instance.onMakeAnchor = AnchorMenu.Instance.GetAnchor;
+ AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations, true);
+ AnchorMenu.Instance.onMakeAnchor = () => AnchorMenu.Instance.GetAnchor(undefined, true);
}
@action
@@ -194,11 +194,11 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
return textRegionAnno;
};
@action
- highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => {
+ highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => {
// creates annotation documents for current highlights
const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DataSym]);
const annotationDoc = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations);
- !savedAnnotations && annotationDoc && this.props.addDocument(annotationDoc);
+ addAsAnnotation && !savedAnnotations && annotationDoc && this.props.addDocument(annotationDoc);
return (annotationDoc as Doc) ?? undefined;
};
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 6d7f2c037..08285ff0c 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable } from 'mobx';
+import { action, computed, observable, trace } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
import * as React from 'react';
@@ -8,15 +8,17 @@ import { Id } from '../../fields/FieldSymbols';
import { NumCast } from '../../fields/Types';
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../Utils';
import { DocUtils } from '../documents/Documents';
+import { DocumentType } from '../documents/DocumentTypes';
import { DragManager } from '../util/DragManager';
import { ScriptingGlobals } from '../util/ScriptingGlobals';
import { Transform } from '../util/Transform';
import { CollectionFreeFormLinksView } from './collections/collectionFreeForm/CollectionFreeFormLinksView';
import { LightboxView } from './LightboxView';
+import { MainView } from './MainView';
import { DocumentView } from './nodes/DocumentView';
import './OverlayView.scss';
import { ScriptingRepl } from './ScriptingRepl';
-import { DefaultStyleProvider } from './StyleProvider';
+import { DefaultStyleProvider, testDocProps } from './StyleProvider';
export type OverlayDisposer = () => void;
@@ -170,74 +172,70 @@ export class OverlayView extends React.Component {
);
@computed get overlayDocs() {
- return LightboxView.LightboxDoc
- ? null
- : DocListCast(Doc.MyOverlayDocs?.data).map(d => {
- let offsetx = 0,
- offsety = 0;
- const dref = React.createRef<HTMLDivElement>();
- const onPointerMove = action((e: PointerEvent, down: number[]) => {
- if (e.buttons === 1) {
- d.overlayX = e.clientX + offsetx;
- d.overlayY = e.clientY + offsety;
- }
- if (e.metaKey) {
- const dragData = new DragManager.DocumentDragData([d]);
- dragData.offset = [-offsetx, -offsety];
- dragData.dropAction = 'move';
- dragData.removeDocument = (doc: Doc | Doc[]) => {
- const docs = doc instanceof Doc ? [doc] : doc;
- docs.forEach(d => Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, d));
- return true;
- };
- dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
- return dragData.removeDocument!(doc) ? addDocument(doc) : false;
- };
- DragManager.StartDocumentDrag([dref.current!], dragData, down[0], down[1]);
- return true;
- }
- return false;
- });
+ return DocListCast(Doc.MyOverlayDocs?.data)
+ .filter(d => !LightboxView.LightboxDoc || d.type === DocumentType.PRES)
+ .map(d => {
+ let offsetx = 0,
+ offsety = 0;
+ const dref = React.createRef<HTMLDivElement>();
+ const onPointerMove = action((e: PointerEvent, down: number[]) => {
+ if (e.buttons === 1) {
+ d.overlayX = e.clientX + offsetx;
+ d.overlayY = e.clientY + offsety;
+ }
+ if (e.metaKey) {
+ const dragData = new DragManager.DocumentDragData([d]);
+ dragData.offset = [-offsetx, -offsety];
+ dragData.dropAction = 'move';
+ dragData.removeDocument = this.removeOverlayDoc;
+ dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
+ return dragData.removeDocument!(doc) ? addDocument(doc) : false;
+ };
+ DragManager.StartDocumentDrag([dref.current!], dragData, down[0], down[1]);
+ return true;
+ }
+ return false;
+ });
- const onPointerDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, onPointerMove, emptyFunction, emptyFunction);
- offsetx = NumCast(d.overlayX) - e.clientX;
- offsety = NumCast(d.overlayY) - e.clientY;
- };
- return (
- <div
- className="overlayView-doc"
- ref={dref}
- key={d[Id]}
- onPointerDown={onPointerDown}
- style={{ top: d.type === 'presentation' ? 0 : undefined, width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.overlayX}px, ${d.overlayY}px)` }}>
- <DocumentView
- Document={d}
- rootSelected={returnTrue}
- bringToFront={emptyFunction}
- addDocument={undefined}
- removeDocument={this.removeOverlayDoc}
- PanelWidth={d[WidthSym]}
- PanelHeight={d[HeightSym]}
- ScreenToLocalTransform={this.docScreenToLocalXf(d)}
- renderDepth={1}
- isDocumentActive={returnTrue}
- isContentActive={returnTrue}
- whenChildContentsActiveChanged={emptyFunction}
- focus={DocUtils.DefaultFocus}
- styleProvider={DefaultStyleProvider}
- docViewPath={returnEmptyDoclist}
- addDocTab={returnFalse}
- pinToPres={emptyFunction}
- docFilters={returnEmptyFilter}
- docRangeFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined}
- />
- </div>
- );
- });
+ const onPointerDown = (e: React.PointerEvent) => {
+ setupMoveUpEvents(this, e, onPointerMove, emptyFunction, emptyFunction);
+ offsetx = NumCast(d.overlayX) - e.clientX;
+ offsety = NumCast(d.overlayY) - e.clientY;
+ };
+ return (
+ <div
+ className="overlayView-doc"
+ ref={dref}
+ key={d[Id]}
+ onPointerDown={onPointerDown}
+ style={{ top: d.type === DocumentType.PRES ? 0 : undefined, width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.overlayX}px, ${d.overlayY}px)` }}>
+ <DocumentView
+ Document={d}
+ rootSelected={returnTrue}
+ bringToFront={emptyFunction}
+ addDocument={undefined}
+ removeDocument={this.removeOverlayDoc}
+ PanelWidth={d[WidthSym]}
+ PanelHeight={d[HeightSym]}
+ ScreenToLocalTransform={this.docScreenToLocalXf(d)}
+ renderDepth={1}
+ isDocumentActive={returnTrue}
+ isContentActive={returnTrue}
+ whenChildContentsActiveChanged={emptyFunction}
+ focus={DocUtils.DefaultFocus}
+ styleProvider={DefaultStyleProvider}
+ docViewPath={returnEmptyDoclist}
+ addDocTab={d.type === DocumentType.PRES ? MainView.addDocTabFunc : returnFalse}
+ pinToPres={emptyFunction}
+ docFilters={returnEmptyFilter}
+ docRangeFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ />
+ </div>
+ );
+ });
}
public static ShowSpinner() {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 8d495d286..411f51d84 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1,6 +1,6 @@
import React = require('react');
import { IconLookup } from '@fortawesome/fontawesome-svg-core';
-import { faAnchor, faArrowRight } from '@fortawesome/free-solid-svg-icons';
+import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Checkbox, Tooltip } from '@material-ui/core';
import { intersection } from 'lodash';
@@ -1680,6 +1680,26 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
</button>
</div>
<div className="propertiesView-input inline">
+ <p>Zoom Text Selections</p>
+ <button
+ style={{ background: !this.sourceAnchor?.followLinkZoomText ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoomText', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
+ <p>Toggle Follow to Outer Context</p>
+ <button
+ style={{ background: !this.sourceAnchor?.followLinkToOuterContext ? '' : '#4476f7', borderRadius: 3 }}
+ onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToOuterContext', this.sourceAnchor)}
+ onClick={e => e.stopPropagation()}
+ className="propertiesButton">
+ <FontAwesomeIcon className="fa-icon" icon={faWindowMaximize as IconLookup} size="lg" />
+ </button>
+ </div>
+ <div className="propertiesView-input inline">
<p>Toggle Target (Show/Hide)</p>
<button
style={{ background: !this.sourceAnchor?.followLinkToggle ? '' : '#4476f7', borderRadius: 3 }}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index ffc004df6..1ead80bd0 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -156,10 +156,13 @@ export class CollectionDockingView extends CollectionSubView() {
}
const tab = Array.from(instance.tabMap.keys()).find(tab => tab.contentItem.config.props.panelName === panelName);
if (tab) {
- tab.header.parent.addChild(newConfig, undefined);
const j = tab.header.parent.contentItems.indexOf(tab.contentItem);
- !addToSplit && j !== -1 && tab.header.parent.contentItems[j].remove();
- return instance.layoutChanged();
+ if (newConfig.props.documentId !== tab.header.parent.contentItems[j].config.props.documentId) {
+ tab.header.parent.addChild(newConfig, undefined);
+ !addToSplit && j !== -1 && tab.header.parent.contentItems[j].remove();
+ return instance.layoutChanged();
+ }
+ return false;
}
return CollectionDockingView.AddSplit(document, panelName, stack, panelName);
}
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index e9bf03208..28f08b6ce 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -190,7 +190,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
CollectionStackedTimeline.SelectingRegion = this;
} else {
this._markerEnd = this.currentTime;
- CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd);
+ CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd, undefined, true);
this._markerEnd = undefined;
CollectionStackedTimeline.SelectingRegion = undefined;
}
@@ -257,7 +257,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
this._markerEnd = tmp;
}
if (!isClick && Math.abs(movement[0]) > 15 && !this.IsTrimming) {
- const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd);
+ const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd, undefined, true);
setTimeout(() => DocumentManager.Instance.getDocumentView(anchor)?.select(false));
}
(!isClick || !wasSelecting) && (this._markerEnd = undefined);
@@ -273,7 +273,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
undefined,
() => {
if (shiftKey) {
- CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime);
+ CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime, undefined, undefined, true);
} else {
!wasPlaying && this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
}
@@ -388,7 +388,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
// creates marker on timeline
@undoBatch
@action
- static createAnchor(rootDoc: Doc, dataDoc: Doc, fieldKey: string, startTag: string, endTag: string, anchorStartTime?: number, anchorEndTime?: number, docAnchor?: Doc) {
+ static createAnchor(rootDoc: Doc, dataDoc: Doc, fieldKey: string, startTag: string, endTag: string, anchorStartTime: Opt<number>, anchorEndTime: Opt<number>, docAnchor: Opt<Doc>, addAsAnnotation: boolean) {
if (anchorStartTime === undefined) return rootDoc;
const anchor =
docAnchor ??
@@ -407,10 +407,12 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
});
Doc.GetProto(anchor)[startTag] = anchorStartTime;
Doc.GetProto(anchor)[endTag] = anchorEndTime;
- if (Cast(dataDoc[fieldKey], listSpec(Doc), null)) {
- Cast(dataDoc[fieldKey], listSpec(Doc), []).push(anchor);
- } else {
- dataDoc[fieldKey] = new List<Doc>([anchor]);
+ if (addAsAnnotation) {
+ if (Cast(dataDoc[fieldKey], listSpec(Doc), null)) {
+ Cast(dataDoc[fieldKey], listSpec(Doc), []).push(anchor);
+ } else {
+ dataDoc[fieldKey] = new List<Doc>([anchor]);
+ }
}
return anchor;
}
@@ -619,17 +621,19 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
);
})}
{!this.IsTrimming && this.selectionContainer}
- <AudioWaveform
- rawDuration={this.props.rawDuration}
- duration={this.clipDuration}
- mediaPath={this.props.mediaPath}
- layoutDoc={this.layoutDoc}
- clipStart={this.clipStart}
- clipEnd={this.clipEnd}
- zoomFactor={this.zoomFactor}
- PanelHeight={this.timelineContentHeight}
- PanelWidth={this.timelineContentWidth}
- />
+ {!this.props.PanelHeight() ? null : (
+ <AudioWaveform
+ rawDuration={this.props.rawDuration}
+ duration={this.clipDuration}
+ mediaPath={this.props.mediaPath}
+ layoutDoc={this.layoutDoc}
+ clipStart={this.clipStart}
+ clipEnd={this.clipEnd}
+ zoomFactor={this.zoomFactor}
+ PanelHeight={this.timelineContentHeight}
+ PanelWidth={this.timelineContentWidth}
+ />
+ )}
{/* {this.renderDictation} */}
<div
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d2074219a..27ae3041f 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -334,7 +334,7 @@ export function CollectionSubView<X>(moreProps?: X) {
const iframe = SelectionManager.Views()[0].ContentDiv?.getElementsByTagName('iframe')?.[0];
const focusNode = iframe?.contentDocument?.getSelection()?.focusNode as any;
if (focusNode) {
- const anchor = srcWeb?.ComponentView?.getAnchor?.();
+ const anchor = srcWeb?.ComponentView?.getAnchor?.(true);
anchor && DocUtils.MakeLink({ doc: htmlDoc }, { doc: anchor });
}
}
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index a1466bcd0..8c613198d 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -32,7 +32,7 @@ export class CollectionTimeView extends CollectionSubView() {
@observable _viewDefDivClick: Opt<ScriptField>;
@observable _focusPivotField: Opt<string>;
- getAnchor = () => {
+ getAnchor = (addAsAnnotation: boolean) => {
const anchor = Docs.Create.HTMLAnchorDocument([], {
title: ComputedField.MakeFunction(`"${this.pivotField}"])`) as any,
annotationOn: this.rootDoc,
@@ -45,11 +45,13 @@ export class CollectionTimeView extends CollectionSubView() {
proto.docRangeFilters = ObjectField.MakeCopy(this.layoutDoc._docRangeFilters as ObjectField) || new List<string>([]);
proto[ViewSpecPrefix + '_viewType'] = this.layoutDoc._viewType;
- // store anchor in annotations list of document (not technically needed since these anchors are never drawn)
- if (Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), null) !== undefined) {
- Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), []).push(anchor);
- } else {
- this.dataDoc[this.props.fieldKey + '-annotations'] = new List<Doc>([anchor]);
+ if (addAsAnnotation) {
+ // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
+ if (Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), null) !== undefined) {
+ Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), []).push(anchor);
+ } else {
+ this.dataDoc[this.props.fieldKey + '-annotations'] = new List<Doc>([anchor]);
+ }
}
return anchor;
};
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index cd58319cb..25fccd89c 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -247,12 +247,13 @@ export class TabDocView extends React.Component<TabDocViewProps> {
alert('Cannot pin presentation document to itself');
return;
}
- const anchorDoc = DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.getAnchor?.();
+ const anchorDoc = DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.getAnchor?.(false);
const pinDoc = Doc.MakeAlias(anchorDoc ?? doc);
pinDoc.presentationTargetDoc = anchorDoc ?? doc;
pinDoc.title = doc.title + ' - Slide';
pinDoc.data = new List<Doc>(); // the children of the alias' layout are the presentation slide children. the alias' data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
pinDoc.presMovement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom;
+ pinDoc.presDuration = 2000;
pinDoc.groupWithUp = false;
pinDoc.context = curPres;
// these should potentially all be props passed down by the CollectionTreeView to the TreeView elements. That way the PresBox could configure all of its children at render time
@@ -273,7 +274,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
pinDoc.presStartTime = NumCast(doc.clipStart);
pinDoc.presEndTime = NumCast(doc.clipEnd, duration);
}
- PresBox.pinDocView(pinDoc, pinProps.pinDocContent ? { ...pinProps, pinData: PresBox.pinDataTypes(doc) } : pinProps, doc);
+ PresBox.pinDocView(pinDoc, pinProps.pinDocContent ? { ...pinProps, pinData: PresBox.pinDataTypes(doc) } : pinProps, pinDoc);
pinDoc.onClick = ScriptField.MakeFunction('navigateToDoc(self.presentationTargetDoc, self)');
Doc.AddDocToList(curPres, 'data', pinDoc, presSelected);
//save position
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 4c8c65707..9811c239b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -204,10 +204,12 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const bclipped = bleft !== b.left || btop !== b.top;
if (aclipped && bclipped) return undefined;
const clipped = aclipped || bclipped;
+ const pt1inside = NumCast(LinkDocs[0].anchor1_x) % 100 !== 0 && NumCast(LinkDocs[0].anchor1_y) % 100 !== 0;
+ const pt2inside = NumCast(LinkDocs[0].anchor2_x) % 100 !== 0 && NumCast(LinkDocs[0].anchor2_y) % 100 !== 0;
const pt1 = [aleft + a.width / 2, atop + a.height / 2];
const pt2 = [bleft + b.width / 2, btop + b.width / 2];
- const pt2vec = [(bDocBounds.left + bDocBounds.right) / 2 - pt2[0], (bDocBounds.top + bDocBounds.bottom) / 2 - pt2[1]];
- const pt1vec = [(aDocBounds.left + aDocBounds.right) / 2 - pt1[0], (aDocBounds.top + aDocBounds.bottom) / 2 - pt1[1]];
+ const pt2vec = pt2inside ? [-0.7071, 0.7071] : [(bDocBounds.left + bDocBounds.right) / 2 - pt2[0], (bDocBounds.top + bDocBounds.bottom) / 2 - pt2[1]];
+ const pt1vec = pt1inside ? [-0.7071, 0.7071] : [(aDocBounds.left + aDocBounds.right) / 2 - pt1[0], (aDocBounds.top + aDocBounds.bottom) / 2 - pt1[1]];
const pt1len = Math.sqrt(pt1vec[0] * pt1vec[0] + pt1vec[1] * pt1vec[1]);
const pt2len = Math.sqrt(pt2vec[0] * pt2vec[0] + pt2vec[1] * pt2vec[1]);
const ptlen = Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1])) / 2;
@@ -288,7 +290,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
stroke="antiquewhite"
strokeWidth="4"
className="collectionfreeformlinkview-linkLine"
- style={{ pointerEvents: 'all', opacity: this._opacity, stroke, strokeWidth }}
+ style={{ pointerEvents: 'visibleStroke', opacity: this._opacity, stroke, strokeWidth }}
onClick={this.onClickLine}
d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
markerEnd={link.linkDisplayArrow ? `url(#${link[Id] + 'arrowhead'})` : ''}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index dc0eb69f3..5cf4cb31f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1565,7 +1565,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
: 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 = () => {
+ getAnchor = (addAsAnnotation: boolean) => {
if (this.props.Document.annotationOn) {
return this.rootDoc;
}
@@ -1574,10 +1574,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const proto = Doc.GetProto(anchor);
proto[ViewSpecPrefix + '_viewType'] = this.layoutDoc._viewType;
proto.docFilters = ObjectField.MakeCopy(this.layoutDoc.docFilters as ObjectField) || new List<string>([]);
- if (Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), null) !== undefined) {
- Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), []).push(anchor);
- } else {
- this.dataDoc[this.props.fieldKey + '-annotations'] = new List<Doc>([anchor]);
+ if (addAsAnnotation) {
+ if (Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), null) !== undefined) {
+ Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), []).push(anchor);
+ } else {
+ this.dataDoc[this.props.fieldKey + '-annotations'] = new List<Doc>([anchor]);
+ }
}
return anchor;
};
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 488f51d77..9581563ce 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -26,33 +26,39 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
render() {
const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: 'auto', width: 19, transform: 'translate(-2px, -2px)' }} />;
- const buttons = [
- <Tooltip key="collect" title={<div className="dash-tooltip">Create a Collection</div>} placement="bottom">
- <button className="antimodeMenu-button" onPointerDown={this.createCollection}>
- <FontAwesomeIcon icon="object-group" size="lg" />
- </button>
- </Tooltip>,
- <Tooltip key="group" title={<div className="dash-tooltip">Create a Grouping</div>} placement="bottom">
- <button className="antimodeMenu-button" onPointerDown={e => this.createCollection(e, true)}>
- <FontAwesomeIcon icon="layer-group" size="lg" />
- </button>
- </Tooltip>,
- <Tooltip key="summarize" title={<div className="dash-tooltip">Summarize Documents</div>} placement="bottom">
- <button className="antimodeMenu-button" onPointerDown={this.summarize}>
- <FontAwesomeIcon icon="compress-arrows-alt" size="lg" />
- </button>
- </Tooltip>,
- <Tooltip key="delete" title={<div className="dash-tooltip">Delete Documents</div>} placement="bottom">
- <button className="antimodeMenu-button" onPointerDown={this.delete}>
- <FontAwesomeIcon icon="trash-alt" size="lg" />
- </button>
- </Tooltip>,
- <Tooltip key="pinWithView" title={<div className="dash-tooltip">Pin selected region to trail</div>} placement="bottom">
- <button className="antimodeMenu-button" onPointerDown={this.pinWithView}>
- {presPinWithViewIcon}
- </button>
- </Tooltip>,
- ];
+ const buttons = (
+ <>
+ <Tooltip key="collect" title={<div className="dash-tooltip">Create a Collection</div>} placement="bottom">
+ <button className="antimodeMenu-button" onPointerDown={this.createCollection}>
+ <FontAwesomeIcon icon="object-group" size="lg" />
+ </button>
+ </Tooltip>
+ ,
+ <Tooltip key="group" title={<div className="dash-tooltip">Create a Grouping</div>} placement="bottom">
+ <button className="antimodeMenu-button" onPointerDown={e => this.createCollection(e, true)}>
+ <FontAwesomeIcon icon="layer-group" size="lg" />
+ </button>
+ </Tooltip>
+ ,
+ <Tooltip key="summarize" title={<div className="dash-tooltip">Summarize Documents</div>} placement="bottom">
+ <button className="antimodeMenu-button" onPointerDown={this.summarize}>
+ <FontAwesomeIcon icon="compress-arrows-alt" size="lg" />
+ </button>
+ </Tooltip>
+ ,
+ <Tooltip key="delete" title={<div className="dash-tooltip">Delete Documents</div>} placement="bottom">
+ <button className="antimodeMenu-button" onPointerDown={this.delete}>
+ <FontAwesomeIcon icon="trash-alt" size="lg" />
+ </button>
+ </Tooltip>
+ ,
+ <Tooltip key="pinWithView" title={<div className="dash-tooltip">Pin selected region to trail</div>} placement="bottom">
+ <button className="antimodeMenu-button" onPointerDown={this.pinWithView}>
+ {presPinWithViewIcon}
+ </button>
+ </Tooltip>
+ </>
+ );
return this.getElement(buttons);
}
}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 7c1137292..dcbadc5f3 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -386,7 +386,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
: ((doc: Doc) => {
Doc.GetProto(doc).data = new List<Doc>(selected);
Doc.GetProto(doc).title = makeGroup ? 'grouping' : 'nested freeform';
- !this.props.isAnnotationOverlay && Doc.AddDocToList(Doc.MyFileOrphans, undefined, Doc.GetProto(doc));
+ !this.props.isAnnotationOverlay && Doc.AddFileOrphan(Doc.GetProto(doc));
doc._panX = doc._panY = 0;
return doc;
})(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 91b63c1a6..a2a2255e6 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -15,10 +15,11 @@ import { LinkManager } from '../../util/LinkManager';
import { SelectionManager } from '../../util/SelectionManager';
import { SettingsManager } from '../../util/SettingsManager';
import { undoBatch } from '../../util/UndoManager';
-import { DocumentView } from '../nodes/DocumentView';
+import { DocumentView, OpenWhere } from '../nodes/DocumentView';
import { LinkDocPreview } from '../nodes/LinkDocPreview';
import './LinkMenuItem.scss';
import React = require('react');
+import { MainView } from '../MainView';
interface LinkMenuItemProps {
groupType: string;
@@ -100,9 +101,15 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
},
emptyFunction,
action(() => {
- SelectionManager.SelectView(this.props.docView, false);
- if ((SettingsManager.propertiesWidth ?? 0) < 100) {
- SettingsManager.propertiesWidth = 250;
+ const trail = DocCast(this.props.docView.rootDoc.presTrail);
+ if (trail) {
+ Doc.ActivePresentation = trail;
+ MainView.addDocTabFunc(trail, OpenWhere.replaceRight);
+ } else {
+ SelectionManager.SelectView(this.props.docView, false);
+ if ((SettingsManager.propertiesWidth ?? 0) < 100) {
+ SettingsManager.propertiesWidth = 250;
+ }
}
})
);
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index a5b5ea664..aa9d648bf 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -137,7 +137,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
return { la1, la2, linkTime };
}
- getAnchor = () => {
+ getAnchor = (addAsAnnotation: boolean) => {
return (
CollectionStackedTimeline.createAnchor(
this.rootDoc,
@@ -145,7 +145,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
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)
+ 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
);
};
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 6f3152981..a39e0f65f 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -135,7 +135,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
DocumentLinksButton.AnnotationId = undefined;
} else if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document) {
const sourceDoc = DocumentLinksButton.StartLink;
- const targetDoc = this.props.View.ComponentView?.getAnchor?.() || this.props.View.Document;
+ const 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;
@@ -183,8 +183,8 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
DocumentLinksButton.AnnotationUri = undefined;
//!this.props.StartLink
} else if (startLink !== endLink) {
- endLink = endLinkView?.docView?._componentView?.getAnchor?.() || endLink;
- startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.() || startLink;
+ endLink = endLinkView?.docView?._componentView?.getAnchor?.(true) || 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);
LinkManager.currentLink = linkDoc;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 95cf08289..b94db2c6b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -121,7 +121,7 @@ export type DocFocusFunc = (doc: Doc, options: DocFocusOptions) => void;
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?: () => 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)
+ 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
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
@@ -696,6 +696,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
});
+ @action
onPointerDown = (e: React.PointerEvent): void => {
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)
@@ -736,6 +737,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
document.removeEventListener('pointerup', this.onPointerUp);
document.addEventListener('pointerup', this.onPointerUp);
+ } else {
+ this._cursorTimer && clearTimeout(this._cursorTimer);
+ this._cursorPress = false;
}
};
@@ -783,9 +787,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@undoBatch
@action
- toggleFollowLink = (location: Opt<string>, zoom?: boolean, setPushpin?: boolean): void => {
+ toggleFollowLink = (location: Opt<string>, zoom?: boolean, setTargetToggle?: boolean): void => {
this.Document.ignoreClick = false;
- if (setPushpin) {
+ if (setTargetToggle) {
this.Document.followLinkToggle = !this.Document.followLinkToggle;
this.Document._isLinkButton = this.Document.followLinkToggle || this.Document._isLinkButton;
} else {
@@ -854,7 +858,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(undefined);
}
if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) {
- const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.() ?? this.props.Document;
+ const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.props.Document;
de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, undefined, undefined, undefined, undefined, [de.x, de.y - 50]);
}
}
@@ -979,7 +983,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
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') + ' Pushpin', event: () => this.toggleFollowLink(undefined, false, true), icon: 'map-pin' });
+ onClicks.push({ description: (this.Document.followLinkToggle ? 'Remove' : 'Make') + ' Target Visibility Toggle', event: () => this.toggleFollowLink(undefined, false, true), icon: 'map-pin' });
onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' });
!existingOnClick && cm.addItem({ description: 'OnClick...', addDivider: true, noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
} else if (DocListCast(this.Document.links).length) {
@@ -1091,7 +1095,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
onClickFunc = () => this.onClickHandler;
setHeight = (height: number) => (this.layoutDoc._height = height);
- setContentView = action((view: { getAnchor?: () => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
+ setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
isContentActive = (outsideReaction?: boolean) => {
return this.props.isContentActive() === false
? false
@@ -1264,7 +1268,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
playAnnotation = () => {
const self = this;
const audioAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations'], listSpec(AudioField), null);
- const anno = audioAnnos.lastElement();
+ const anno = audioAnnos?.lastElement();
if (anno instanceof AudioField && this.audioAnnoState === 'stopped') {
new Howl({
src: [anno.url.href],
@@ -1690,7 +1694,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return this.props.PanelHeight();
}
@computed get Xshift() {
- return this.effectiveNativeWidth ? (this.props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2 : 0;
+ return this.effectiveNativeWidth ? Math.max(0, (this.props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2) : 0;
}
@computed get Yshift() {
return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 && (!this.layoutDoc.nativeHeightUnfrozen || (!this.fitWidth && this.effectiveNativeHeight * this.nativeScaling <= this.props.PanelHeight()))
@@ -1707,10 +1711,13 @@ export class DocumentView extends React.Component<DocumentViewProps> {
toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight());
focus = (doc: Doc, options: DocFocusOptions) => this.docView?.focus(doc, options);
getBounds = () => {
- if (!this.docView || !this.docView.ContentDiv || this.props.Document.presBox || this.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
+ if (!this.docView || !this.docView.ContentDiv || this.props.Document.type === DocumentType.PRES || this.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
return undefined;
}
- const xf = this.docView?.props.ScreenToLocalTransform().scale(this.nativeScaling).inverse();
+ const xf = this.docView?.props
+ .ScreenToLocalTransform()
+ .scale(this.trueNativeWidth() ? this.nativeScaling : 1)
+ .inverse();
const [[left, top], [right, bottom]] = [xf.transformPoint(0, 0), xf.transformPoint(this.panelWidth, this.panelHeight)];
if (this.docView.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
const docuBox = this.docView.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
@@ -1781,11 +1788,12 @@ export class DocumentView extends React.Component<DocumentViewProps> {
PanelHeight = () => this.panelHeight;
NativeDimScaling = () => this.nativeScaling;
selfView = () => this;
+ trueNativeWidth = () => returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, false));
screenToLocalTransform = () =>
this.props
.ScreenToLocalTransform()
.translate(-this.centeringX, -this.centeringY)
- .scale(1 / this.nativeScaling);
+ .scale(this.trueNativeWidth() ? 1 / this.nativeScaling : 1);
componentDidMount() {
this._disposers.reactionScript = reaction(
() => ScriptCast(this.rootDoc.reactionScript)?.script?.run({ this: this.props.Document, self: Cast(this.rootDoc, Doc, null) || this.props.Document }).result,
@@ -1831,6 +1839,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
const xshift = Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined;
const yshift = Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined;
const isButton = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear;
+
return (
<div
className="contentFittingDocumentView"
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 04d252abe..78ba499ec 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -33,7 +33,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
constructor(props: Readonly<FieldViewProps>) {
super(props);
const targetDoc = FilterBox.targetDoc;
- if (targetDoc && !targetDoc.currentFilter) targetDoc.currentFilter = FilterBox.createFilterDoc();
+ if (targetDoc && !targetDoc.currentFilter) runInAction(() => (targetDoc.currentFilter = FilterBox.createFilterDoc()));
}
/// creates a new, empty filter doc
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index 24562ccbd..5c0005dae 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -12,7 +12,7 @@ import { TraceMobx } from '../../../fields/util';
import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { undoBatch } from '../../util/UndoManager';
-import { ViewBoxBaseComponent } from '../DocComponent';
+import { ViewBoxAnnotatableComponent } from '../DocComponent';
import { DocFocusOptions } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
@@ -22,7 +22,7 @@ type EquationDocument = makeInterface<[typeof EquationSchema, typeof documentSch
const EquationDocument = makeInterface(EquationSchema, documentSchema);
@observer
-export class FunctionPlotBox extends ViewBoxBaseComponent<FieldViewProps>() {
+export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(FunctionPlotBox, fieldKey);
}
@@ -41,10 +41,11 @@ export class FunctionPlotBox extends ViewBoxBaseComponent<FieldViewProps>() {
() => this.createGraph()
);
}
- getAnchor = () => {
+ 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));
+ if (addAsAnnotation) this.addDocument(anchor);
return anchor;
};
@action
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index ac953d13b..e2ecca0b6 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -52,7 +52,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
private _dropDisposer?: DragManager.DragDropDisposer;
private _disposers: { [name: string]: IReactionDisposer } = {};
- private _getAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
+ private _getAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = () => undefined;
@observable _curSuffix = '';
@observable _uploadIcon = uploadIcons.idle;
@@ -86,13 +86,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
: 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 = () => {
+ getAnchor = (addAsAnnotation: boolean) => {
const anchor =
- this._getAnchor?.(this._savedAnnotations) ?? // use marquee anchor, otherwise, save zoom/pan as 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 });
if (anchor) {
PresBox.pinDocView(anchor, { pinData: { pannable: true, dataview: true, dataannos: true } }, this.rootDoc);
- this.addDocument(anchor);
+ addAsAnnotation && this.addDocument(anchor);
return anchor;
}
return this.rootDoc;
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 10897b48f..a2143f629 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -36,9 +36,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
this._timeout && clearTimeout(this._timeout);
}
- getAnchor = () => {
- return this.rootDoc;
- };
+ 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/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index b2fdd93fc..1eab06381 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -4,7 +4,7 @@ import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import wiki from 'wikijs';
import { Doc, DocCastAsync, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/Types';
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils';
import { DocServer } from '../../DocServer';
import { Docs, DocUtils } from '../../documents/Documents';
@@ -17,7 +17,8 @@ import { Transform } from '../../util/Transform';
import { DocumentView, DocumentViewSharedProps, OpenWhere } from './DocumentView';
import './LinkDocPreview.scss';
import React = require('react');
-import { SelectionManager } from '../../util/SelectionManager';
+import { SearchUtil } from '../../util/SearchUtil';
+import { SearchBox } from '../search/SearchBox';
interface LinkDocPreviewProps {
linkDoc?: Doc;
@@ -37,6 +38,8 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
LinkDocPreview.LinkInfo !== info && (LinkDocPreview.LinkInfo = info);
}
+ static _instance: Opt<LinkDocPreview>;
+
_infoRef = React.createRef<HTMLDivElement>();
_linkDocRef = React.createRef<HTMLDivElement>();
@observable public static LinkInfo: Opt<LinkDocPreviewProps>;
@@ -47,6 +50,11 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
@observable _toolTipText = '';
@observable _hrefInd = 0;
+ constructor(props: any) {
+ super(props);
+ LinkDocPreview._instance = this;
+ }
+
@action init() {
var linkTarget = this.props.linkDoc;
this._linkSrc = this.props.linkSrc;
@@ -63,6 +71,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
this._markerTargetDoc = this._targetDoc = linkTarget;
}
this._toolTipText = '';
+ this.updateHref();
}
componentDidUpdate(props: any) {
if (props.linkSrc !== this.props.linkSrc || props.linkDoc !== this.props.linkDoc || props.hrefs !== this.props.hrefs) this.init();
@@ -72,6 +81,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
document.addEventListener('pointerdown', this.onPointerDown, true);
}
+ @action
componentWillUnmount() {
LinkDocPreview.SetLinkInfo(undefined);
document.removeEventListener('pointerdown', this.onPointerDown, true);
@@ -81,7 +91,8 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
!this._linkDocRef.current?.contains(e.target as any) && LinkDocPreview.Clear(); // close preview when not clicking anywhere other than the info bar of the preview
};
- @computed get href() {
+ @action
+ updateHref() {
if (this.props.hrefs?.length) {
const href = this.props.hrefs[this._hrefInd];
if (href.indexOf(Doc.localServerPath()) !== 0) {
@@ -91,33 +102,33 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
.page(href.replace('https://en.wikipedia.org/wiki/', ''))
.then(page => page.summary().then(action(summary => (this._toolTipText = summary.substring(0, 500)))));
} else {
- setTimeout(action(() => (this._toolTipText = 'url => ' + href)));
+ this._toolTipText = 'url => ' + href;
}
} else {
// hyperlink to a document .. decode doc id and retrieve from the server. this will trigger vals() being invalidated
- const anchorDoc = href.replace(Doc.localServerPath(), '').split('?')[0];
- anchorDoc &&
- DocServer.GetRefField(anchorDoc).then(
- action(anchor => {
- if (anchor instanceof Doc && DocListCast(anchor.links).length) {
- this._linkDoc = this._linkDoc ?? DocListCast(anchor.links)[0];
- const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords;
- if (automaticLink) {
- // automatic links specify the target in the link info, not the source
- const linkTarget = anchor;
- this._linkSrc = LinkManager.getOppositeAnchor(this._linkDoc, linkTarget);
- this._markerTargetDoc = this._targetDoc = linkTarget;
- } else {
- this._linkSrc = anchor;
- const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
- this._markerTargetDoc = linkTarget;
- this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget;
- }
- this._toolTipText = '';
- if (LinkDocPreview.LinkInfo?.noPreview) this.followLink();
+ const anchorDocId = href.replace(Doc.localServerPath(), '').split('?')[0];
+ const anchorDoc = anchorDocId ? PromiseValue(DocCast(DocServer.GetCachedRefField(anchorDocId) ?? DocServer.GetRefField(anchorDocId))) : undefined;
+ anchorDoc?.then?.(
+ action(anchor => {
+ if (anchor instanceof Doc && DocListCast(anchor.links).length) {
+ this._linkDoc = this._linkDoc ?? DocListCast(anchor.links)[0];
+ const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords;
+ if (automaticLink) {
+ // automatic links specify the target in the link info, not the source
+ const linkTarget = anchor;
+ this._linkSrc = LinkManager.getOppositeAnchor(this._linkDoc, linkTarget);
+ this._markerTargetDoc = this._targetDoc = linkTarget;
+ } else {
+ this._linkSrc = anchor;
+ const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
+ this._markerTargetDoc = linkTarget;
+ this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget;
}
- })
- );
+ this._toolTipText = 'link to ' + this._targetDoc?.title;
+ if (LinkDocPreview.LinkInfo?.noPreview || this._linkSrc?.followLinkToggle || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink();
+ }
+ })
+ );
}
return href;
}
@@ -159,11 +170,14 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
};
followLink = () => {
+ LinkDocPreview.Clear();
if (this._linkDoc && this._linkSrc) {
- LinkDocPreview.Clear();
LinkFollower.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false);
} else if (this.props.hrefs?.length) {
- this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }), OpenWhere.addRight);
+ const webDoc =
+ Array.from(SearchBox.staticSearchCollection(Doc.MyFilesystem, this.props.hrefs[0]).keys()).lastElement() ??
+ Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true });
+ this.props.docProps?.addDocTab(webDoc, OpenWhere.lightbox);
}
};
@@ -209,7 +223,6 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
}
@computed get docPreview() {
- const href = this.href; // needs to be here to trigger lookup of web pages and docs on server
return (!this._linkDoc || !this._targetDoc || !this._linkSrc) && !this._toolTipText ? null : (
<div className="linkDocPreview-inner">
{!this.props.showHeader ? null : this.previewHeader}
@@ -286,7 +299,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
className="linkDocPreview"
ref={this._linkDocRef}
onPointerDown={this.followLinkPointerDown}
- style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
+ style={{ display: !this._toolTipText ? 'none' : undefined, left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
{this.docPreview}
</div>
);
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index c8d5b0154..95cb49037 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -522,10 +522,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
);
}
- getAnchor = () => {
- const anchor = AnchorMenu.Instance?.GetAnchor(this._savedAnnotations) ?? this.rootDoc;
- return anchor;
- };
+ getAnchor = (addAsAnnotation: boolean) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc;
/**
* render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index e22ee5021..b88ac113e 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -212,7 +212,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
PresBox.restoreTargetDocView(this.props.DocumentView?.(), {}, doc, options.zoomTime ?? 500, { pannable: doc.presPinData ? true : false });
return this._pdfViewer?.scrollFocus(doc, NumCast(doc.presPinViewScroll, NumCast(doc.y)), options) ?? (didToggle ? 1 : undefined);
};
- getAnchor = () => {
+ getAnchor = (addAsAnnotation: boolean) => {
let ele: Opt<HTMLDivElement> = undefined;
if (this._pdfViewer?.selectionContent()) {
ele = document.createElement('div');
@@ -226,10 +226,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
PresBox.pinDocView(anchor, { pinData: { scrollable: true, pannable: true } }, this.rootDoc);
return anchor;
};
- const anchor = this._pdfViewer?._getAnchor(this._pdfViewer.savedAnnotations()) ?? docAnchor();
+ const annoAnchor = this._pdfViewer?._getAnchor(this._pdfViewer.savedAnnotations(), true);
+ const anchor = annoAnchor ?? docAnchor();
anchor.text = ele?.textContent ?? '';
anchor.textHtml = ele?.innerHTML;
- this.addDocument(anchor);
+ if (addAsAnnotation || annoAnchor) {
+ this.addDocument(anchor);
+ }
return anchor;
};
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 76a24d831..f94996c66 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -128,9 +128,9 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
this.setupDictation();
}
}
- getAnchor = () => {
+ 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) || this.rootDoc;
+ return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow', '_timecodeToHide', startTime, startTime === undefined ? undefined : startTime + 3, undefined, addAsAnnotation) || this.rootDoc;
};
videoLoad = () => {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index a8f78edd5..1dfa55c64 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -385,15 +385,18 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc));
Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc));
this.props.addDocument?.(imageSummary);
- const link = DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor() }, 'video snapshot');
+ const link = DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor(true) }, 'video snapshot');
link && (Doc.GetProto(link.anchor2 as Doc).timecodeToHide = NumCast((link.anchor2 as Doc).timecodeToShow) + 3);
setTimeout(() => downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, 'move', true));
};
- getAnchor = () => {
+ getAnchor = (addAsAnnotation: boolean) => {
const timecode = Cast(this.layoutDoc._currentTimecode, 'number', null);
- const marquee = AnchorMenu.Instance.GetAnchor?.();
- return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow' /* videoStart */, '_timecodeToHide' /* videoEnd */, timecode ? timecode : undefined, undefined, marquee) || this.rootDoc;
+ 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
+ );
};
// sets video info on load
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 8be4884ce..acf4fe4b0 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -307,7 +307,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return undefined;
};
- getAnchor = () => {
+ getAnchor = (addAsAnnotation: boolean) => {
let ele: Opt<HTMLDivElement> = undefined;
try {
const contents = this._iframe?.contentWindow?.getSelection()?.getRangeAt(0).cloneContents();
@@ -317,7 +317,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
} catch (e) {}
const anchor =
- this._getAnchor(this._savedAnnotations) ??
+ this._getAnchor(this._savedAnnotations, false) ??
Docs.Create.WebanchorDocument(this._url, {
title: StrCast(this.rootDoc.title + ' ' + this.layoutDoc._scrollTop),
y: NumCast(this.layoutDoc._scrollTop),
@@ -325,7 +325,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
});
anchor.text = ele?.textContent ?? '';
anchor.textHtml = ele?.innerHTML;
- this.addDocumentWrapper(anchor);
+ addAsAnnotation && this.addDocumentWrapper(anchor);
return anchor;
};
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index e477d7ae2..1de29f806 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -161,7 +161,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
</div>
);
return (
- <div className={`menuButton ${this.type} ${numBtnType}`} onClick={action(() => (this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen))}>
+ <div className={`menuButton ${this.type} ${numBtnType}`} onPointerDown={e => e.stopPropagation()} onClick={action(() => (this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen))}>
{checkResult}
{label}
{this.rootDoc.dropDownOpen ? dropdown : null}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 63347015b..39005a18b 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -287,12 +287,12 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
document.addEventListener('pointerdown', hideMenu, true);
};
render() {
- return this.getElement([
+ return this.getElement(
<Tooltip key="trash" title={<div className="dash-tooltip">{`Show Pivot Viewer for '${this._fieldKey}'`}</div>}>
<button className="antimodeMenu-button" onPointerDown={this.showFields}>
<FontAwesomeIcon icon="eye" size="lg" />
</button>
- </Tooltip>,
- ]);
+ </Tooltip>
+ );
}
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index b895043de..8407eee96 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -231,7 +231,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
}
- getAnchor = () => this.makeLinkAnchor(undefined, 'add:right', undefined, 'Anchored Selection', false);
+ getAnchor = (addAsAnnotation: boolean) => this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation);
@action
setupAnchorMenu = () => {
@@ -239,23 +239,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
AnchorMenu.Instance.OnClick = (e: PointerEvent) => {
!this.layoutDoc.showSidebar && this.toggleSidebar();
- setTimeout(() => this._sidebarRef.current?.anchorMenuClick(this.makeLinkAnchor(undefined, 'add:right', undefined, 'Anchored Selection', true))); // give time for sidebarRef to be created
+ setTimeout(() => this._sidebarRef.current?.anchorMenuClick(this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', true))); // give time for sidebarRef to be created
};
AnchorMenu.Instance.OnAudio = (e: PointerEvent) => {
!this.layoutDoc.showSidebar && this.toggleSidebar();
- const anchor = this.getAnchor();
- const target = this._sidebarRef.current?.anchorMenuClick(anchor);
- if (target) {
- anchor.followLinkAudio = true;
- DocumentViewInternal.recordAudioAnnotation(Doc.GetProto(target), Doc.LayoutFieldKey(target));
- target.title = ComputedField.MakeFunction(`self["text-audioAnnotations-text"].lastElement()`);
- }
+ const anchor = this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', true, true);
+ setTimeout(() => {
+ const target = this._sidebarRef.current?.anchorMenuClick(anchor);
+ if (target) {
+ anchor.followLinkAudio = true;
+ DocumentViewInternal.recordAudioAnnotation(Doc.GetProto(target), Doc.LayoutFieldKey(target));
+ target.title = ComputedField.MakeFunction(`self["text-audioAnnotations-text"].lastElement()`);
+ }
+ });
};
AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => {
this._editorView?.state && RichTextMenu.Instance.setHighlight(color, this._editorView, this._editorView?.dispatch);
return undefined;
});
- AnchorMenu.Instance.onMakeAnchor = this.getAnchor;
+ AnchorMenu.Instance.onMakeAnchor = () => this.getAnchor(true);
AnchorMenu.Instance.StartCropDrag = unimplementedFunction;
/**
* This function is used by the PDFmenu to create an anchor highlight and a new linked text annotation.
@@ -270,7 +272,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
return target;
};
- DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docViewPath().lastElement(), this.getAnchor, targetCreator), e.pageX, e.pageY);
+ DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docViewPath().lastElement(), () => this.getAnchor(true), targetCreator), e.pageX, e.pageY);
});
const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to);
this.props.isSelected(true) && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom);
@@ -518,7 +520,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
@undoBatch
@action
drop = (e: Event, de: DragManager.DropEvent) => {
- if (de.complete.annoDragData) de.complete.annoDragData.dropDocCreator = this.getAnchor;
+ if (de.complete.annoDragData) de.complete.annoDragData.dropDocCreator = () => this.getAnchor(true);
const dragData = de.complete.docDragData;
if (dragData) {
const draggedDoc = dragData.draggedDocuments.length && dragData.draggedDocuments[0];
@@ -689,9 +691,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
pinToPres = (anchor: Doc) => this.props.pinToPres(anchor, {});
@undoBatch
- makePushpin = (anchor: Doc) => (anchor.followLinkToggle = !anchor.followLinkToggle);
+ makeTargetToggle = (anchor: Doc) => (anchor.followLinkToggle = !anchor.followLinkToggle);
+
+ @undoBatch
+ showTargetTrail = (anchor: Doc) => {
+ const trail = DocCast(anchor.presTrail);
+ if (trail) {
+ Doc.ActivePresentation = trail;
+ this.props.addDocTab(trail, OpenWhere.replaceRight);
+ }
+ };
- isPushpin = (anchor: Doc) => BoolCast(anchor.followLinkToggle);
+ isTargetToggler = (anchor: Doc) => BoolCast(anchor.followLinkToggle);
specificContextMenu = (e: React.MouseEvent): void => {
const cm = ContextMenu.Instance;
@@ -714,8 +725,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
AnchorMenu.Instance.Delete = () => this.deleteAnnotation(anchor as Doc);
AnchorMenu.Instance.Pinned = false;
AnchorMenu.Instance.PinToPres = () => this.pinToPres(anchor as Doc);
- AnchorMenu.Instance.MakePushpin = () => this.makePushpin(anchor as Doc);
- AnchorMenu.Instance.IsPushpin = () => this.isPushpin(anchor as Doc);
+ AnchorMenu.Instance.MakeTargetToggle = () => this.makeTargetToggle(anchor as Doc);
+ AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(anchor as Doc);
+ AnchorMenu.Instance.IsTargetToggler = () => this.isTargetToggler(anchor as Doc);
AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
})
);
@@ -891,7 +903,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
};
// TODO: nda -- Look at how link anchors are added
- makeLinkAnchor(anchorDoc?: Doc, location?: string, targetHref?: string, title?: string, noPreview?: boolean) {
+ makeLinkAnchor(anchorDoc?: Doc, location?: string, targetHref?: string, title?: string, noPreview?: boolean, addAsAnnotation?: boolean) {
const state = this._editorView?.state;
if (state) {
let selectedText = '';
@@ -901,7 +913,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (sel.from !== sel.to) {
const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: '#' + this._editorView?.state.doc.textBetween(sel.from, sel.to), annotationOn: this.dataDoc, unrendered: true });
const href = targetHref ?? Doc.localServerPath(anchor);
- if (anchor !== anchorDoc) this.addDocument(anchor);
+ if (anchor !== anchorDoc && addAsAnnotation) this.addDocument(anchor);
tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
const allAnchors = [{ href, title, anchorId: anchor[Id] }];
@@ -1469,7 +1481,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (!this._editorView?.state.selection.empty && !(this._editorView?.state.selection instanceof NodeSelection) && FormattedTextBox._canAnnotate && !(e.nativeEvent as any).dash) this.setupAnchorMenu();
if (!this._downEvent) return;
this._downEvent = false;
- if (this.props.isContentActive(true) && !(e.nativeEvent as any).dash) {
+ if (this._editorView?.state.selection.empty && this.props.isContentActive(true) && !(e.nativeEvent as any).dash) {
const editor = this._editorView!;
const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
!this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0))));
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index e3c67ad2e..5675776fb 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -259,7 +259,7 @@ export class RichTextRules {
this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3))));
}
const target = (docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500 }, docid);
- DocUtils.MakeLink({ doc: this.TextBox.getAnchor() }, { doc: target }, 'portal to:portal from', undefined);
+ DocUtils.MakeLink({ doc: this.TextBox.getAnchor(true) }, { doc: target }, 'portal to:portal from', undefined);
const fstate = this.TextBox.EditorView?.state;
if (fstate && selection) {
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 8d43c33f0..3898490d3 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -108,7 +108,7 @@ export const marks: { [index: string]: MarkSpec } = {
node.attrs.title,
],
]
- : ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, 'data-noPreview': node.attrs.noPreview, location: node.attrs.location, style: `text-decoration: underline` }, 0];
+ : ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, 'data-noPreview': node.attrs.noPreview, location: node.attrs.location, style: `text-decoration: underline; cursor: default` }, 0];
},
},
diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss
index 4d60a02f1..fd202590e 100644
--- a/src/client/views/nodes/trails/PresBox.scss
+++ b/src/client/views/nodes/trails/PresBox.scss
@@ -938,14 +938,19 @@
min-width: 15px;
max-width: 100px;
left: 8px;
+ margin: auto;
+ margin-left: unset;
+ height: 100%;
}
.presBox-presentPanel {
display: flex;
justify-self: end;
width: 100%;
- max-width: 300px;
- min-width: 150px;
+ margin: auto;
+ margin-right: unset;
+ height: 100%;
+ position: relative;
}
select {
@@ -955,7 +960,7 @@
.presBox-button {
cursor: pointer;
- height: 25px;
+ //height: 100%;
border-radius: 5px;
display: none;
justify-content: center;
@@ -986,6 +991,9 @@
width: max-content;
position: absolute;
right: 10px;
+ margin: auto;
+ margin-right: unset;
+ height: 100%;
.present-icon {
margin-right: 7px;
@@ -999,9 +1007,16 @@
position: relative;
display: inline-block;
left: 8px;
+ margin: auto;
+ margin-left: unset;
}
}
+.presBox-buttons.inOverlay {
+ padding-top: unset;
+ padding-bottom: unset;
+}
+
.presBox-backward,
.presBox-forward {
width: 25px;
@@ -1052,6 +1067,8 @@
font-size: 30px;
position: absolute;
min-width: 50px;
+ margin: auto;
+ margin-left: unset;
}
}
@@ -1087,13 +1104,12 @@
color: $white;
border-radius: 5px;
grid-template-rows: 100%;
- height: 25;
+ height: 100%;
width: max-content;
min-width: max-content;
justify-content: space-evenly;
align-items: center;
display: flex;
- position: absolute;
transition: all 0.2s;
.presPanel-button-text {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 6f3dc5e82..ff05dcdcb 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -12,7 +12,7 @@ import { ObjectField } from '../../../../fields/ObjectField';
import { listSpec } from '../../../../fields/Schema';
import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
import { AudioField } from '../../../../fields/URLField';
-import { emptyPath, returnFalse, returnOne, setupMoveUpEvents, StopEvent } from '../../../../Utils';
+import { emptyFunction, emptyPath, returnFalse, returnOne, setupMoveUpEvents, StopEvent } from '../../../../Utils';
import { DocServer } from '../../../DocServer';
import { Docs } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
@@ -104,7 +104,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
return NumCast(this.rootDoc._itemIndex);
}
@computed get activeItem() {
- return Cast(this.childDocs[NumCast(this.rootDoc._itemIndex)], Doc, null);
+ return DocCast(this.childDocs[NumCast(this.rootDoc._itemIndex)]);
}
@computed get targetDoc() {
return Cast(this.activeItem?.presentationTargetDoc, Doc, null);
@@ -129,13 +129,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
isActiveItemTarget = (layoutDoc: Doc) => this.activeItem?.presentationTargetDoc === layoutDoc;
clearSelectedArray = () => this.selectedArray.clear();
- addToSelectedArray = (doc: Doc) => this.selectedArray.add(doc);
- removeFromSelectedArray = (doc: Doc) => this.selectedArray.delete(doc);
+ addToSelectedArray = action((doc: Doc) => this.selectedArray.add(doc));
+ removeFromSelectedArray = action((doc: Doc) => this.selectedArray.delete(doc));
_unmounting = false;
@action
componentWillUnmount() {
this._unmounting = true;
+ if (this._presTimer) clearTimeout(this._presTimer);
document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
this.resetPresentation();
// Turn of progressivize editors
@@ -143,7 +144,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
Object.values(this._disposers).forEach(disposer => disposer?.());
}
- @action
componentDidMount() {
this._disposers.keyboard = reaction(
() => this.selectedDoc,
@@ -167,10 +167,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
);
this.props.setContentView?.(this);
this._unmounting = false;
- this.rootDoc._forceRenderEngine = computeTimelineLayout.name;
- this.layoutDoc.presStatus = PresStatus.Edit;
- this.layoutDoc._gridGap = 0;
- this.layoutDoc._yMargin = 0;
+ if (this.props.renderDepth > 0) {
+ runInAction(() => {
+ this.rootDoc._forceRenderEngine = computeTimelineLayout.name;
+ this.layoutDoc._gridGap = 0;
+ this.layoutDoc._yMargin = 0;
+ });
+ }
this.turnOffEdit(true);
this._disposers.selection = reaction(
() => SelectionManager.Views(),
@@ -208,12 +211,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
nextSlide = (slideNum?: number) => {
const nextSlideInd = slideNum ?? this.itemIndex + 1;
let curSlideInd = nextSlideInd;
- const resetSelection = action(() => {
- this.clearSelectedArray();
- for (let i = nextSlideInd; i <= curSlideInd; i++) {
- this.addToSelectedArray(this.childDocs[i]);
- }
- });
CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.());
this.clearSelectedArray();
const doGroupWithUp =
@@ -221,6 +218,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
() => {
if (nextSelected < this.childDocs.length) {
if (force || this.childDocs[nextSelected].groupWithUp) {
+ this.addToSelectedArray(this.childDocs[nextSelected]);
const serial = nextSelected + 1 < this.childDocs.length && NumCast(this.childDocs[nextSelected + 1].groupWithUp) > 1;
if (serial) {
this.gotoDocument(nextSelected, this.activeItem, true, async () => {
@@ -229,7 +227,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
doGroupWithUp(nextSelected + 1)();
});
} else {
- this.gotoDocument(nextSelected, this.activeItem, undefined, resetSelection);
+ this.gotoDocument(nextSelected, this.activeItem, true);
curSlideInd = this.itemIndex;
doGroupWithUp(nextSelected + 1)();
}
@@ -247,9 +245,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const slides = DocListCast(this.rootDoc[StrCast(this.presFieldKey, 'data')]);
const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d)))) : this.itemIndex;
this.nextSlide(curLast + 1 === this.childDocs.length ? (this.layoutDoc.presLoop ? 0 : curLast) : curLast + 1);
- } else if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presStatus === PresStatus.Edit)) {
- // Case 2: Last slide and presLoop is toggled ON or it is in Edit mode
- this.nextSlide(0);
+ } else {
+ if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presStatus === PresStatus.Edit)) {
+ // Case 2: Last slide and presLoop is toggled ON or it is in Edit mode
+ this.nextSlide(0);
+ }
+ return 0;
}
return this.itemIndex;
};
@@ -446,7 +447,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
pinDoc.presWidth = NumCast(targetDoc.width);
pinDoc.presHeight = NumCast(targetDoc.height);
}
- if (pinProps.pinAudioPlay) pinDoc.followLinkAudio = true;
+ if (pinProps.pinAudioPlay) pinDoc.presPlayAudio = true;
if (pinProps.pinData) {
pinDoc.presPinData =
pinProps.pinData.scrollable ||
@@ -507,46 +508,44 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
console.log('Finish Slide Nav: ' + targetDoc.title);
targetDoc[AnimationSym] = undefined;
};
- const srcContext = Cast(targetDoc.context, Doc, null) ?? Cast(Cast(targetDoc.annotationOn, Doc, null)?.context, Doc, null);
- const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
- const collectionDocView = presCollection ? DocumentManager.Instance.getDocumentView(presCollection) : undefined;
- const includesDoc: boolean = DocumentManager.Instance.getDocumentView(targetDoc) ? true : false; // DocListCast(presCollection?.data).includes(targetDoc);
- const tabMap = CollectionDockingView.Instance?.tabMap;
- const tab = tabMap && Array.from(tabMap).find(tab => tab.DashDoc === srcContext || tab.DashDoc === targetDoc);
- // Handles the setting of presCollection
- if (includesDoc) {
- //Case 1: Pres collection should not change as it is already the same
- } else if (tab !== undefined) {
- // Case 2: Pres collection should update
- this.layoutDoc.presCollection = srcContext;
- }
- const presStatus = this.rootDoc.presStatus;
const selViewCache = Array.from(this.selectedArray);
const dragViewCache = Array.from(this._dragArray);
const eleViewCache = Array.from(this._eleArray);
const resetSelection = action(() => {
- const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc);
- if (presDocView) SelectionManager.SelectView(presDocView, false);
- this.rootDoc.presStatus = presStatus;
- this.clearSelectedArray();
- selViewCache.forEach(doc => this.addToSelectedArray(doc));
- this._dragArray.splice(0, this._dragArray.length, ...dragViewCache);
- this._eleArray.splice(0, this._eleArray.length, ...eleViewCache);
+ if (!this.props.isSelected()) {
+ const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc);
+ if (presDocView) SelectionManager.SelectView(presDocView, false);
+ this.clearSelectedArray();
+ selViewCache.forEach(doc => this.addToSelectedArray(doc));
+ this._dragArray.splice(0, this._dragArray.length, ...dragViewCache);
+ this._eleArray.splice(0, this._eleArray.length, ...eleViewCache);
+ }
finished();
});
const createDocView = (doc: Doc, finished?: () => void) => {
DocumentManager.Instance.AddViewRenderedCb(doc, () => finished?.());
- (collectionDocView ?? this).props.addDocTab(doc, OpenWhere.lightbox);
- this.layoutDoc.presCollection = targetDoc;
+ LightboxView.AddDocTab(doc, OpenWhere.lightbox);
};
- PresBox.NavigateToTarget(targetDoc, activeItem, createDocView, srcContext, includesDoc || tab ? finished : resetSelection);
+ PresBox.NavigateToTarget(targetDoc, activeItem, createDocView, resetSelection);
};
- static NavigateToTarget(targetDoc: Doc, activeItem: Doc, createDocView: any, srcContext: Doc, finished?: () => void) {
+ static NavigateToTarget(targetDoc: Doc, activeItem: Doc, createDocView: any, finished?: () => void) {
if (activeItem.presMovement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) {
(DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.();
return;
}
+ const options: DocFocusOptions = {
+ willPan: activeItem.presMovement !== PresMovement.None,
+ willPanZoom: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center,
+ zoomScale: activeItem.presMovement === PresMovement.Center ? 0 : NumCast(activeItem.presZoom, 1),
+ zoomTime: activeItem.presMovement === PresMovement.Jump ? 0 : NumCast(activeItem.presTransition, 500),
+ effect: activeItem,
+ noSelect: true,
+ originatingDoc: activeItem,
+ easeFunc: StrCast(activeItem.presEaseFunc, 'ease') as any,
+ zoomTextSelections: 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.
@@ -556,34 +555,29 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
PresBox.restoreTargetDocView(DocumentManager.Instance.getFirstDocumentView(targetDoc), { pinDocLayout }, activeItem, NumCast(activeItem.presTransition, 500));
}
};
- // If openDocument is selected then it should open the document for the user
- if (activeItem.openDocument) {
- LightboxView.SetLightboxDoc(targetDoc); // openInTab(targetDoc);
- setTimeout(restoreLayout);
- } else {
- if (targetDoc) {
- LightboxView.SetLightboxDoc(undefined);
- const options: DocFocusOptions = {
- willPan: activeItem.presMovement !== PresMovement.None,
- willPanZoom: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center,
- zoomScale: activeItem.presMovement === PresMovement.Center ? 0 : NumCast(activeItem.presZoom, 1),
- zoomTime: activeItem.presMovement === PresMovement.Jump ? 0 : NumCast(activeItem.presTransition, 500),
- effect: activeItem,
- noSelect: true,
- originatingDoc: activeItem,
- easeFunc: StrCast(activeItem.presEaseFunc, 'ease') as any,
- zoomTextSelections: true
- };
- if (activeItem.presentationTargetDoc instanceof Doc) activeItem.presentationTargetDoc[AnimationSym] = undefined;
- var containerDocContext = srcContext ? [srcContext] : [];
- while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) {
- containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
- }
+ const finishAndRestoreLayout = () => {
+ finished?.();
+ restoreLayout();
+ };
+ const containerDocContext = DocumentManager.GetContextPath(targetDoc);
- DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finished);
+ 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);
}
- restoreLayout();
}
+ 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)) {
+ LightboxView.SetLightboxDoc(undefined);
+ }
+ DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finishAndRestoreLayout);
+ });
+ } else finishAndRestoreLayout();
}
/**
@@ -596,58 +590,56 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const curDoc = Cast(doc, Doc, null);
const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc);
- if (tagDoc === this.layoutDoc.presCollection) {
- tagDoc.opacity = 1;
- } else {
- if (curDoc.presHide) {
- if (index !== this.itemIndex) {
- tagDoc.opacity = 1;
- }
+ if (curDoc.presHide) {
+ if (index !== this.itemIndex) {
+ tagDoc.opacity = 1;
}
- const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex);
- if (curDoc.presHideBefore && index === hidingIndBef) {
- if (index > this.itemIndex) {
- tagDoc.opacity = 0;
- } else if (index === this.itemIndex || !curDoc.presHideAfter) {
- tagDoc.opacity = 1;
- }
+ }
+ const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex);
+ if (curDoc.presHideBefore && index === hidingIndBef) {
+ if (index > this.itemIndex) {
+ tagDoc.opacity = 0;
+ } else if (index === this.itemIndex || !curDoc.presHideAfter) {
+ tagDoc.opacity = 1;
}
- const hidingIndAft = itemIndexes
- .slice()
- .reverse()
- .find(item => item < this.itemIndex);
- if (curDoc.presHideAfter && index === hidingIndAft) {
- if (index < this.itemIndex) {
- tagDoc.opacity = 0;
- } else if (index === this.itemIndex || !curDoc.presHideBefore) {
- tagDoc.opacity = 1;
- }
+ }
+ const hidingIndAft = itemIndexes
+ .slice()
+ .reverse()
+ .find(item => item < this.itemIndex);
+ if (curDoc.presHideAfter && index === hidingIndAft) {
+ if (index < this.itemIndex) {
+ tagDoc.opacity = 0;
+ } else if (index === this.itemIndex || !curDoc.presHideBefore) {
+ tagDoc.opacity = 1;
}
- const hidingInd = itemIndexes.find(item => item === this.itemIndex);
- if (curDoc.presHide && index === hidingInd) {
- if (index === this.itemIndex) {
- tagDoc.opacity = 0;
- }
+ }
+ const hidingInd = itemIndexes.find(item => item === this.itemIndex);
+ if (curDoc.presHide && index === hidingInd) {
+ if (index === this.itemIndex) {
+ tagDoc.opacity = 0;
}
}
});
};
- //The function that starts or resets presentaton functionally, depending on presStatus of the layoutDoc
- @action
- startAutoPres = async (startSlide: number) => {
- if (!this.childDocs.length) return;
- this.layoutDoc.presStatus = PresStatus.Autoplay;
- this.startPresentation(startSlide + 1 === this.childDocs.length ? 0 : startSlide);
- clearTimeout(this._presTimer);
- const func = (itemIndex: number) => {
- if (itemIndex === this.next()) this.layoutDoc.presStatus = PresStatus.Manual;
- this._presTimer = setTimeout(
- () => this.layoutDoc.presStatus !== PresStatus.Manual && func(this.itemIndex),
- NumCast(this.activeItem.presDuration, this.activeItem.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem.presTransition)
- );
+ _exitTrail: Opt<() => void>;
+ PlayTrail = (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) }));
+ 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;
+ });
+ LightboxView.SetLightboxDoc(undefined);
+ Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc);
+ return PresStatus.Edit;
};
- func(this.itemIndex);
};
// The function pauses the auto presentation
@@ -696,40 +688,54 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
* @param startIndex: index that the presentation will start at
*/
startPresentation = (startIndex: number) => {
- this.childDocs.forEach(doc => {
- const tagDoc = doc.presentationTargetDoc as Doc;
- if (doc.presHideBefore && this.childDocs.indexOf(doc) > startIndex) {
- tagDoc.opacity = 0;
- }
- if (doc.presHideAfter && this.childDocs.indexOf(doc) < startIndex) {
- tagDoc.opacity = 0;
- }
- });
- this.gotoDocument(startIndex, this.activeItem);
+ clearTimeout(this._presTimer);
+ if (this.childDocs.length) {
+ this.layoutDoc.presStatus = PresStatus.Autoplay;
+ this.childDocs.forEach(doc => {
+ const tagDoc = doc.presentationTargetDoc as Doc;
+ if (doc.presHideBefore && this.childDocs.indexOf(doc) > startIndex) tagDoc.opacity = 0;
+ if (doc.presHideAfter && this.childDocs.indexOf(doc) < startIndex) tagDoc.opacity = 0;
+ // if (doc.presHide && this.childDocs.indexOf(doc) === startIndex) tagDoc.opacity = 0;
+ });
+ const func = () => {
+ const delay = NumCast(this.activeItem.presDuration, this.activeItem.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem.presTransition);
+ this._presTimer = setTimeout(() => {
+ if (!this.next()) this.layoutDoc.presStatus = this._exitTrail?.() ?? PresStatus.Manual;
+ this.layoutDoc.presStatus === PresStatus.Autoplay && func();
+ }, delay);
+ };
+ this.gotoDocument(startIndex, this.activeItem, undefined, func);
+ }
};
/**
* The method called to open the presentation as a minimized view
*/
@action
- updateMinimize = async () => {
+ enterMinimize = () => {
+ clearTimeout(this._presTimer);
+ const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+ this.props.removeDocument?.(this.layoutDoc);
+ return PresBox.OpenPresMinimized(this.rootDoc, [pt[0] + (this.props.PanelWidth() - 250), pt[1] + 10]);
+ };
+ exitMinimize = () => {
if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
- this.layoutDoc.presStatus = PresStatus.Edit;
Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc);
CollectionDockingView.AddSplit(this.rootDoc, OpenWhereMod.right);
- } else {
- this.layoutDoc.presStatus = PresStatus.Edit;
- clearTimeout(this._presTimer);
- const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
- this.rootDoc.overlayX = pt[0] + (this.props.PanelWidth() - 250);
- this.rootDoc.overlayY = pt[1] + 10;
- this.rootDoc._height = 30;
- this.rootDoc._width = 248;
- Doc.AddDocToList(Doc.MyOverlayDocs, undefined, this.rootDoc);
- this.props.removeDocument?.(this.layoutDoc);
}
+ return PresStatus.Edit;
};
+ public static minimizedWidth = 198;
+ public static OpenPresMinimized(doc: Doc, pt: number[]) {
+ doc.overlayX = pt[0];
+ doc.overlayY = pt[1];
+ doc._height = 30;
+ doc._width = PresBox.minimizedWidth;
+ Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc);
+ return (doc.presStatus = PresStatus.Manual);
+ }
+
/**
* Called when the user changes the view type
* Either 'List' (stacking) or 'Slides' (carousel)
@@ -863,8 +869,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
selectElement = async (doc: Doc) => {
CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.());
this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
- if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(DocCast(doc.context)), 0);
- else this.updateCurrentPresentation(DocCast(doc.context));
+ // if (doc.presPinView) setTimeout(() => this.updateCurrentPresentation(DocCast(doc.context)), 0);
+ // else
+ this.updateCurrentPresentation(DocCast(doc.context));
};
//Command click
@@ -941,11 +948,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
break;
case 'Escape':
if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
- this.updateMinimize();
- } else if (this.layoutDoc.presStatus === 'edit') {
+ this.exitClicked();
+ } else if (this.layoutDoc.presStatus === PresStatus.Edit) {
this.clearSelectedArray();
this._eleArray.length = this._dragArray.length = 0;
- } else this.layoutDoc.presStatus = 'edit';
+ } else {
+ this.layoutDoc.presStatus = PresStatus.Edit;
+ }
if (this._presTimer) clearTimeout(this._presTimer);
handled = true;
break;
@@ -1010,7 +1019,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get order() {
const order: JSX.Element[] = [];
const docs: Doc[] = [];
- const presCollection = Cast(this.rootDoc.presCollection, Doc, null);
+ const presCollection = DocumentManager.GetContextPath(this.activeItem).reverse().lastElement();
const dv = DocumentManager.Instance.getDocumentView(presCollection);
this.childDocs
.filter(doc => Cast(doc.presentationTargetDoc, Doc, null))
@@ -1176,8 +1185,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@undoBatch
@action
updateOpenDoc = (activeItem: Doc) => {
- activeItem.openDocument = !activeItem.openDocument;
- this.selectedArray.forEach(doc => (doc.openDocument = activeItem.openDocument));
+ activeItem.presOpenInLightbox = !activeItem.presOpenInLightbox;
+ this.selectedArray.forEach(doc => (doc.presOpenInLightbox = activeItem.presOpenInLightbox));
};
@undoBatch
@action
@@ -1222,8 +1231,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get transitionDropdown() {
const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
- const isPresCollection: boolean = targetDoc === this.layoutDoc.presCollection;
- const isPinWithView: boolean = BoolCast(activeItem.presPinView);
const presEffect = (effect: PresEffect) => (
<div className={`presBox-dropdownOption ${activeItem.presEffect === effect || (effect === PresEffect.None && !activeItem.presEffect) ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateEffect(effect)}>
{effect}
@@ -1250,7 +1257,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const type = targetDoc.type;
const transitionSpeed = activeItem.presTransition ? NumCast(activeItem.presTransition) / 1000 : 0.5;
const zoom = NumCast(activeItem.presZoom, 1) * 100;
- let duration = activeItem.presDuration ? NumCast(activeItem.presDuration) / 1000 : 2;
+ let duration = activeItem.presDuration ? NumCast(activeItem.presDuration) / 1000 : 0;
if (activeItem.type === DocumentType.AUDIO) duration = NumCast(activeItem.duration);
const effect = activeItem.presEffect ? activeItem.presEffect : PresMovement.None;
// activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : PresMovement.Zoom;
@@ -1276,11 +1283,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
{this.movementName(activeItem)}
<FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openMovementDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
<div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={StopEvent} style={{ display: this._openMovementDropdown ? 'grid' : 'none' }}>
- {isPresCollection || (isPresCollection && isPinWithView) ? null : presMovement(PresMovement.None)}
+ {presMovement(PresMovement.None)}
{presMovement(PresMovement.Center)}
{presMovement(PresMovement.Zoom)}
{presMovement(PresMovement.Pan)}
- {isPresCollection || (isPresCollection && isPinWithView) ? null : presMovement(PresMovement.Jump)}
+ {presMovement(PresMovement.Jump)}
</div>
</div>
<div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
@@ -1322,29 +1329,25 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div className="ribbon-box">
Visibility {'&'} Duration
<div className="ribbon-doubleButton">
- {isPresCollection ? null : (
- <Tooltip title={<div className="dash-tooltip">{'Hide before presented'}</div>}>
- <div className={`ribbon-toggle ${activeItem.presHideBefore ? 'active' : ''}`} onClick={() => this.updateHideBefore(activeItem)}>
- Hide before
- </div>
- </Tooltip>
- )}
- {isPresCollection ? null : (
- <Tooltip title={<div className="dash-tooltip">{'Hide while presented'}</div>}>
- <div className={`ribbon-toggle ${activeItem.presHide ? 'active' : ''}`} onClick={() => this.updateHide(activeItem)}>
- Hide
- </div>
- </Tooltip>
- )}
- {isPresCollection ? null : (
- <Tooltip title={<div className="dash-tooltip">{'Hide after presented'}</div>}>
- <div className={`ribbon-toggle ${activeItem.presHideAfter ? 'active' : ''}`} onClick={() => this.updateHideAfter(activeItem)}>
- Hide after
- </div>
- </Tooltip>
- )}
+ <Tooltip title={<div className="dash-tooltip">{'Hide before presented'}</div>}>
+ <div className={`ribbon-toggle ${activeItem.presHideBefore ? 'active' : ''}`} onClick={() => this.updateHideBefore(activeItem)}>
+ Hide before
+ </div>
+ </Tooltip>
+ <Tooltip title={<div className="dash-tooltip">{'Hide while presented'}</div>}>
+ <div className={`ribbon-toggle ${activeItem.presHide ? 'active' : ''}`} onClick={() => this.updateHide(activeItem)}>
+ Hide
+ </div>
+ </Tooltip>
+
+ <Tooltip title={<div className="dash-tooltip">{'Hide after presented'}</div>}>
+ <div className={`ribbon-toggle ${activeItem.presHideAfter ? 'active' : ''}`} onClick={() => this.updateHideAfter(activeItem)}>
+ Hide after
+ </div>
+ </Tooltip>
+
<Tooltip title={<div className="dash-tooltip">{'Open in lightbox view'}</div>}>
- <div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? Colors.LIGHT_BLUE : '' }} onClick={() => this.updateOpenDoc(activeItem)}>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presOpenInLightbox ? Colors.LIGHT_BLUE : '' }} onClick={() => this.updateOpenDoc(activeItem)}>
Lightbox
</div>
</Tooltip>
@@ -1379,44 +1382,46 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</>
)}
</div>
- {isPresCollection ? null : (
- <div className="ribbon-box">
- Effects
- <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
- <div className="presBox-subheading">Play Audio Annotation</div>
- <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.followLinkAudio = !BoolCast(activeItem.followLinkAudio))} checked={BoolCast(activeItem.followLinkAudio)} />
- </div>
- <div
- className="presBox-dropdown"
- onClick={action(e => {
- e.stopPropagation();
- this._openEffectDropdown = !this._openEffectDropdown;
- })}
- style={{ borderBottomLeftRadius: this._openEffectDropdown ? 0 : 5, border: this._openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
- {effect?.toString()}
- <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
- <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this._openEffectDropdown ? 'grid' : 'none' }} onPointerDown={e => e.stopPropagation()}>
- {presEffect(PresEffect.None)}
- {presEffect(PresEffect.Fade)}
- {presEffect(PresEffect.Flip)}
- {presEffect(PresEffect.Rotate)}
- {presEffect(PresEffect.Bounce)}
- {presEffect(PresEffect.Roll)}
- </div>
- </div>
- <div className="ribbon-doubleButton" style={{ display: effect === PresEffectDirection.None ? 'none' : 'inline-flex' }}>
- <div className="presBox-subheading">Effect direction</div>
- <div className="ribbon-property">{StrCast(this.activeItem.presEffectDirection)}</div>
- </div>
- <div className="effectDirection" style={{ display: effect === PresEffectDirection.None ? 'none' : 'grid', width: 40 }}>
- {presDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
- {presDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
- {presDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
- {presDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
- {presDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
+ <div className="ribbon-box">
+ Effects
+ <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
+ <div className="presBox-subheading">Play Audio Annotation</div>
+ <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presPlayAudio = !BoolCast(activeItem.presPlayAudio))} checked={BoolCast(activeItem.presPlayAudio)} />
+ </div>
+ <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
+ <div className="presBox-subheading">Zoom Text Selections</div>
+ <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presZoomText = !BoolCast(activeItem.presZoomText))} checked={BoolCast(activeItem.presZoomText)} />
+ </div>
+ <div
+ className="presBox-dropdown"
+ onClick={action(e => {
+ e.stopPropagation();
+ this._openEffectDropdown = !this._openEffectDropdown;
+ })}
+ style={{ borderBottomLeftRadius: this._openEffectDropdown ? 0 : 5, border: this._openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
+ {effect?.toString()}
+ <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
+ <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this._openEffectDropdown ? 'grid' : 'none' }} onPointerDown={e => e.stopPropagation()}>
+ {presEffect(PresEffect.None)}
+ {presEffect(PresEffect.Fade)}
+ {presEffect(PresEffect.Flip)}
+ {presEffect(PresEffect.Rotate)}
+ {presEffect(PresEffect.Bounce)}
+ {presEffect(PresEffect.Roll)}
</div>
</div>
- )}
+ <div className="ribbon-doubleButton" style={{ display: effect === PresEffectDirection.None ? 'none' : 'inline-flex' }}>
+ <div className="presBox-subheading">Effect direction</div>
+ <div className="ribbon-property">{StrCast(this.activeItem.presEffectDirection)}</div>
+ </div>
+ <div className="effectDirection" style={{ display: effect === PresEffectDirection.None ? 'none' : 'grid', width: 40 }}>
+ {presDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
+ {presDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
+ {presDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
+ {presDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
+ {presDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
+ </div>
+ </div>
<div className="ribbon-final-box">
<div className="ribbon-final-button-hidden" onClick={() => this.applyTo(this.childDocs)}>
Apply to all
@@ -1621,7 +1626,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get newDocumentToolbarDropdown() {
return (
<div
- className={'presBox-toolbar-dropdown'}
+ className="presBox-toolbar-dropdown"
style={{ display: this._newDocumentTools && this.layoutDoc.presStatus === 'edit' ? 'inline-flex' : 'none' }}
onClick={e => e.stopPropagation()}
onPointerUp={e => e.stopPropagation()}
@@ -1757,7 +1762,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (freeform && layout) doc = this.createTemplate(layout, title);
if (!freeform && !layout) doc = Docs.Create.TextDocument('', { _nativeWidth: 400, _width: 225, title: title });
if (doc) {
- const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
+ const tabMap = CollectionDockingView.Instance?.tabMap;
+ const tab = tabMap && Array.from(tabMap).find(tab => tab.DashDoc.type === DocumentType.COL)?.DashDoc;
+ const presCollection = DocumentManager.GetContextPath(this.activeItem).reverse().lastElement().presentationTargetDoc ?? tab;
const data = Cast(presCollection?.data, listSpec(Doc));
const presData = Cast(this.rootDoc.data, listSpec(Doc));
if (data && presData) {
@@ -1798,7 +1805,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
className="dropdown-play-button"
onClick={undoBatch(
action(() => {
- this.updateMinimize();
+ this.enterMinimize();
this.turnOffEdit(true);
this.gotoDocument(this.itemIndex, this.activeItem);
})
@@ -1821,7 +1828,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
scrollFocus = () => {
- this.startOrPause(false);
+ // this.gotoDocument(0);
+ // this.startOrPause(false);
return undefined;
};
@@ -1951,8 +1959,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get topPanel() {
const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
const isMini: boolean = this.toolbarWidth <= 100;
+ const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc);
return (
- <div className="presBox-buttons" style={{ background: Doc.ActivePresentation === this.rootDoc ? Colors.LIGHT_BLUE : undefined, display: !this.rootDoc._chromeHidden ? 'none' : undefined }}>
+ <div className={`presBox-buttons${inOverlay ? ' inOverlay' : ''}`} style={{ background: Doc.ActivePresentation === this.rootDoc ? Colors.LIGHT_BLUE : undefined, display: !this.rootDoc._chromeHidden ? 'none' : undefined }}>
{isMini ? null : (
<select className="presBox-viewPicker" style={{ display: this.layoutDoc.presStatus === 'edit' ? 'block' : 'none' }} onPointerDown={e => e.stopPropagation()} onChange={this.viewChanged} value={mode}>
<option onPointerDown={StopEvent} value={CollectionViewType.Stacking}>
@@ -1969,7 +1978,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</select>
)}
<div className="presBox-presentPanel" style={{ opacity: this.childDocs.length ? 1 : 0.3 }}>
- <span className={`presBox-button ${this.layoutDoc.presStatus === 'edit' ? 'present' : ''}`}>
+ <span className={`presBox-button ${this.layoutDoc.presStatus === PresStatus.Edit ? 'present' : ''}`}>
<div
className="presBox-button-left"
onClick={undoBatch(() => {
@@ -2001,11 +2010,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get playButtons() {
const presEnd: boolean = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1;
const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0;
+ const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc);
// Case 1: There are still other frames and should go through all frames before going to next slide
return (
<div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== 'edit' ? 'inline-flex' : 'none' }}>
<Tooltip title={<div className="dash-tooltip">{'Loop'}</div>}>
- <div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : 'white' }} onClick={() => (this.layoutDoc.presLoop = !this.layoutDoc.presLoop)}>
+ <div
+ className="presPanel-button"
+ style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : 'white' }}
+ onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => (this.layoutDoc.presLoop = !this.layoutDoc.presLoop), false, false)}>
<FontAwesomeIcon icon={'redo-alt'} />
</div>
</Tooltip>
@@ -2013,42 +2026,77 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div
className="presPanel-button"
style={{ opacity: presStart ? 0.4 : 1 }}
- onClick={e => {
- this.back();
- if (this._presTimer) {
- clearTimeout(this._presTimer);
- this.layoutDoc.presStatus = PresStatus.Manual;
- }
- e.stopPropagation();
- }}>
+ onPointerDown={e =>
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ emptyFunction,
+ () => {
+ this.back();
+ if (this._presTimer) {
+ clearTimeout(this._presTimer);
+ this.layoutDoc.presStatus = PresStatus.Manual;
+ }
+ e.stopPropagation();
+ },
+ false,
+ false
+ )
+ }>
<FontAwesomeIcon icon={'arrow-left'} />
</div>
<Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}>
- <div className="presPanel-button" onClick={e => this.startOrPause(true)}>
+ <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.startOrPause(true), false, false)}>
<FontAwesomeIcon icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? 'pause' : 'play'} />
</div>
</Tooltip>
<div
className="presPanel-button"
style={{ opacity: presEnd ? 0.4 : 1 }}
- onClick={e => {
- this.next();
- if (this._presTimer) {
- clearTimeout(this._presTimer);
- this.layoutDoc.presStatus = PresStatus.Manual;
- }
- e.stopPropagation();
- }}>
+ onPointerDown={e =>
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ emptyFunction,
+ () => {
+ this.next();
+ if (this._presTimer) {
+ clearTimeout(this._presTimer);
+ this.layoutDoc.presStatus = PresStatus.Manual;
+ }
+ e.stopPropagation();
+ },
+ false,
+ false
+ )
+ }>
<FontAwesomeIcon icon={'arrow-right'} />
</div>
<div className="presPanel-divider"></div>
<Tooltip title={<div className="dash-tooltip">{'Click to return to 1st slide'}</div>}>
- <div className="presPanel-button" style={{ border: 'solid 1px white' }} onClick={() => this.nextSlide(0)}>
+ <div
+ className="presPanel-button"
+ style={{ border: 'solid 1px white' }}
+ onPointerDown={e =>
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ emptyFunction,
+ () => {
+ this.nextSlide(0);
+ },
+ false,
+ false
+ )
+ }>
<b>1</b>
</div>
</Tooltip>
- <div className="presPanel-button-text" onClick={() => this.gotoDocument(0, this.activeItem)} style={{ display: this.props.PanelWidth() > 250 ? 'inline-flex' : 'none' }}>
- Slide {this.itemIndex + 1} / {this.childDocs.length}
+ <div className="presPanel-button-text" onClick={() => this.gotoDocument(0, this.activeItem)} style={{ display: inOverlay || this.props.PanelWidth() > 250 ? 'inline-flex' : 'none' }}>
+ {`${inOverlay ? '' : 'Slide'} ${this.itemIndex + 1} / ${this.childDocs.length}`}
</div>
<div className="presPanel-divider"></div>
{this.props.PanelWidth() > 250 ? (
@@ -2056,14 +2104,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
className="presPanel-button-text"
onClick={undoBatch(
action(() => {
- this.layoutDoc.presStatus = 'edit';
+ this.layoutDoc.presStatus = PresStatus.Edit;
clearTimeout(this._presTimer);
})
)}>
EXIT
</div>
) : (
- <div className="presPanel-button" onClick={undoBatch(action(() => (this.layoutDoc.presStatus = 'edit')))}>
+ <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, this.exitClicked, false, false)}>
<FontAwesomeIcon icon={'times'} />
</div>
)}
@@ -2073,13 +2121,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@action
startOrPause = (makeActive = true) => {
- if (this.itemIndex + 1 === this.childDocs.length) {
- this.gotoDocument(0);
- } else {
- makeActive && this.updateCurrentPresentation();
- if (this.layoutDoc.presStatus === PresStatus.Manual || this.layoutDoc.presStatus === PresStatus.Edit) this.startAutoPres(this.itemIndex);
- else this.pauseAutoPres();
- }
+ makeActive && this.updateCurrentPresentation();
+ if (this.layoutDoc.presStatus === PresStatus.Manual || this.layoutDoc.presStatus === PresStatus.Edit) this.startPresentation(this.itemIndex);
+ else this.pauseAutoPres();
};
@action
@@ -2101,9 +2145,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
@undoBatch
@action
- exitClicked = (e: PointerEvent) => {
- this.updateMinimize();
- this.layoutDoc.presStatus = PresStatus.Edit;
+ exitClicked = () => {
+ this.layoutDoc.presStatus = this._exitTrail?.() ?? this.exitMinimize();
clearTimeout(this._presTimer);
};
@@ -2138,8 +2181,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
// needed to ensure that the childDocs are loaded for looking up fields
this.childDocs.slice();
const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
- const presEnd: boolean = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1;
- const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0;
+ const presEnd = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1;
+ const presStart = !this.layoutDoc.presLoop && this.itemIndex === 0;
+ const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc);
return this.props.addDocTab === returnFalse ? ( // bcz: hack!! - addDocTab === returnFalse only when this is being rendered by the OverlayView which means the doc is a mini player
<div className="miniPres" onClick={e => e.stopPropagation()} onPointerEnter={action(e => (this._forceKeyEvents = true))}>
<div
@@ -2181,7 +2225,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>
</div>
) : (
- <div className="presBox-cont" style={{ minWidth: DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc) ? 240 : undefined }}>
+ <div className="presBox-cont" style={{ minWidth: inOverlay ? PresBox.minimizedWidth : undefined }}>
{this.topPanel}
{this.toolbar}
{this.newDocumentToolbarDropdown}
@@ -2226,12 +2270,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
);
}
static NavigateToDoc(bestTarget: Doc, activeItem: Doc) {
- const srcContext = Cast(bestTarget.context, Doc, null) ?? Cast(Cast(bestTarget.annotationOn, Doc, null)?.context, Doc, null);
const openInTab = (doc: Doc, finished?: () => void) => {
CollectionDockingView.AddSplit(doc, OpenWhereMod.right);
finished?.();
};
- PresBox.NavigateToTarget(bestTarget, activeItem, openInTab, srcContext);
+ PresBox.NavigateToTarget(bestTarget, activeItem, openInTab);
}
}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 5e1474b89..788900b46 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -50,9 +50,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get collapsedHeight() {
return 35;
} // the collapsed height changes depending on the state of the presBox. We could store this on the presentation element template if it's used by only one presentation - but if it's shared by multiple, then this value must be looked up
- @computed get presStatus() {
- return this.presBox.presStatus;
- }
@computed get selectedArray() {
return this.presBoxView?.selectedArray;
}
@@ -364,19 +361,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
@computed get recordingIsInOverlay() {
- let isInOverlay = false;
- DocListCast(Doc.MyOverlayDocs.data).forEach(doc => {
- if (doc.slides === this.rootDoc) {
- isInOverlay = true;
- // return
- }
- });
- return isInOverlay;
+ return DocListCast(Doc.MyOverlayDocs.data).some(doc => doc.slides === this.rootDoc);
}
// a previously recorded video will have timecode defined
- static videoIsRecorded = (activeItem: Doc) => {
- const casted = Cast(activeItem.recording, Doc, null);
+ static videoIsRecorded = (activeItem: Opt<Doc>) => {
+ const casted = Cast(activeItem?.recording, Doc, null);
return casted && 'currentTimecode' in casted;
};
@@ -391,10 +381,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
static removeEveryExistingRecordingInOverlay = () => {
// Remove every recording that already exists in overlay view
DocListCast(Doc.MyOverlayDocs.data).forEach(doc => {
- // if it's a recording video, don't remove from overlay (user can lose data)
- if (!PresElementBox.videoIsRecorded(DocCast(doc.slides))) return;
-
if (doc.slides !== null) {
+ // if it's a recording video, don't remove from overlay (user can lose data)
+ if (!PresElementBox.videoIsRecorded(DocCast(doc.slides))) return;
+
Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, doc);
}
});
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 265328036..c53cc608c 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -17,6 +17,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
static Instance: AnchorMenu;
private _disposer: IReactionDisposer | undefined;
+ private _disposer2: IReactionDisposer | undefined;
private _commentCont = React.createRef<HTMLButtonElement>();
private _palette = [
'rgba(208, 2, 27, 0.8)',
@@ -36,9 +37,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
'rgba(0, 0, 0, 0.8)',
];
- @observable private _keyValue: string = '';
- @observable private _valueValue: string = '';
- @observable private _added: boolean = false;
@observable private highlightColor: string = 'rgba(245, 230, 95, 0.616)';
@observable private _showLinkPopup: boolean = false;
@@ -52,13 +50,13 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
public OnAudio: (e: PointerEvent) => void = unimplementedFunction;
public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
- public Highlight: (color: string, isPushpin: boolean) => Opt<Doc> = (color: string, isPushpin: boolean) => undefined;
- public GetAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
+ public Highlight: (color: string, isTargetToggler: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => Opt<Doc> = (color: string, isTargetToggler: boolean) => undefined;
+ public GetAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => undefined;
public Delete: () => void = unimplementedFunction;
- public AddTag: (key: string, value: string) => boolean = returnFalse;
public PinToPres: () => void = unimplementedFunction;
- public MakePushpin: () => void = unimplementedFunction;
- public IsPushpin: () => boolean = returnFalse;
+ public MakeTargetToggle: () => void = unimplementedFunction;
+ public ShowTargetTrail: () => void = unimplementedFunction;
+ public IsTargetToggler: () => boolean = returnFalse;
public get Active() {
return this._left > 0;
}
@@ -70,7 +68,17 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
AnchorMenu.Instance._canFade = false;
}
+ componentWillUnmount() {
+ this._disposer?.();
+ this._disposer2?.();
+ }
+
componentDidMount() {
+ this._disposer2 = reaction(
+ () => this._opacity,
+ opacity => !opacity && (this._showLinkPopup = false),
+ { fireImmediately: true }
+ );
this._disposer = reaction(
() => SelectionManager.Views(),
selected => {
@@ -112,7 +120,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@action
highlightClicked = (e: React.MouseEvent) => {
- if (!this.Highlight(this.highlightColor, false) && this.Pinned) {
+ if (!this.Highlight(this.highlightColor, false, undefined, true) && this.Pinned) {
this.Highlighting = !this.Highlighting;
}
AnchorMenu.Instance.fadeOut(true);
@@ -174,82 +182,62 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
this.highlightColor = Utils.colorString(col);
};
- @action keyChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
- this._keyValue = e.currentTarget.value;
- };
- @action valueChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
- this._valueValue = e.currentTarget.value;
- };
- @action addTag = (e: React.PointerEvent) => {
- if (this._keyValue.length > 0 && this._valueValue.length > 0) {
- this._added = this.AddTag(this._keyValue, this._valueValue);
- setTimeout(
- action(() => (this._added = false)),
- 1000
- );
- }
- };
-
render() {
const buttons =
- this.Status === 'marquee'
- ? [
- this.highlighter,
-
- <Tooltip key="annotate" title={<div className="dash-tooltip">{'Drag to Place Annotation'}</div>}>
- <button className="antimodeMenu-button annotate" ref={this._commentCont} onPointerDown={this.pointerDown} style={{ cursor: 'grab' }}>
- <FontAwesomeIcon icon="comment-alt" size="lg" />
- </button>
- </Tooltip>,
- AnchorMenu.Instance.OnAudio === unimplementedFunction ? (
- <></>
- ) : (
- <Tooltip key="annoaudiotate" title={<div className="dash-tooltip">{'Click to Record Annotation'}</div>}>
- <button className="antimodeMenu-button annotate" onPointerDown={this.audioDown} style={{ cursor: 'grab' }}>
- <FontAwesomeIcon icon="microphone" size="lg" />
- </button>
- </Tooltip>
- ),
- <Tooltip key="link" title={<div className="dash-tooltip">{'Find document to link to selected text'}</div>}>
- <button className="antimodeMenu-button link" onPointerDown={this.toggleLinkPopup} style={{}}>
- <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(1.5)' }} icon={'search'} size="lg" />
- <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(0.5)', transformOrigin: 'top left', top: 12, left: 12 }} icon={'link'} size="lg" />
- </button>
- </Tooltip>,
- <LinkPopup key="popup" showPopup={this._showLinkPopup} linkCreateAnchor={this.onMakeAnchor} />,
- AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? (
- <></>
- ) : (
- <Tooltip key="crop" title={<div className="dash-tooltip">{'Click/Drag to create cropped image'}</div>}>
- <button className="antimodeMenu-button annotate" onPointerDown={this.cropDown} style={{ cursor: 'grab' }}>
- <FontAwesomeIcon icon="image" size="lg" />
- </button>
- </Tooltip>
- ),
- ]
- : [
- <Tooltip key="trash" title={<div className="dash-tooltip">{'Remove Link Anchor'}</div>}>
- <button className="antimodeMenu-button" onPointerDown={this.Delete}>
- <FontAwesomeIcon icon="trash-alt" size="lg" />
- </button>
- </Tooltip>,
- <Tooltip key="Pin" title={<div className="dash-tooltip">{'Pin to Presentation'}</div>}>
- <button className="antimodeMenu-button" onPointerDown={this.PinToPres}>
- <FontAwesomeIcon icon="map-pin" size="lg" />
- </button>
- </Tooltip>,
- <Tooltip key="pushpin" title={<div className="dash-tooltip">{'toggle pushpin behavior'}</div>}>
- <button className="antimodeMenu-button" style={{ color: this.IsPushpin() ? 'black' : 'white', backgroundColor: this.IsPushpin() ? 'white' : 'black' }} onPointerDown={this.MakePushpin}>
- <FontAwesomeIcon icon="thumbtack" size="lg" />
- </button>
- </Tooltip>,
- // <div key="7" className="anchorMenu-addTag" >
- // <input onChange={this.keyChanged} placeholder="Key" style={{ gridColumn: 1 }} />
- // <input onChange={this.valueChanged} placeholder="Value" style={{ gridColumn: 3 }} />
- // </div>,
- // <button key="8" className="antimodeMenu-button" title={`Add tag: ${this._keyValue} with value: ${this._valueValue}`} onPointerDown={this.addTag}>
- // <FontAwesomeIcon style={{ transition: "all .2s" }} color={this._added ? "#42f560" : "white"} icon="check" size="lg" /></button>,
- ];
+ this.Status === 'marquee' ? (
+ <>
+ {this.highlighter}
+ <Tooltip key="annotate" title={<div className="dash-tooltip">Drag to Place Annotation</div>}>
+ <button className="antimodeMenu-button annotate" ref={this._commentCont} onPointerDown={this.pointerDown} style={{ cursor: 'grab' }}>
+ <FontAwesomeIcon icon="comment-alt" size="lg" />
+ </button>
+ </Tooltip>
+ {AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : (
+ <Tooltip key="annoaudiotate" title={<div className="dash-tooltip">Click to Record Annotation</div>}>
+ <button className="antimodeMenu-button annotate" onPointerDown={this.audioDown} style={{ cursor: 'grab' }}>
+ <FontAwesomeIcon icon="microphone" size="lg" />
+ </button>
+ </Tooltip>
+ )}
+ <Tooltip key="link" title={<div className="dash-tooltip">Find document to link to selected text</div>}>
+ <button className="antimodeMenu-button link" onPointerDown={this.toggleLinkPopup}>
+ <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(1.5)' }} icon={'search'} size="lg" />
+ <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(0.5)', transformOrigin: 'top left', top: 12, left: 12 }} icon={'link'} size="lg" />
+ </button>
+ </Tooltip>
+ <LinkPopup key="popup" showPopup={this._showLinkPopup} linkCreateAnchor={this.onMakeAnchor} />,
+ {AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? null : (
+ <Tooltip key="crop" title={<div className="dash-tooltip">Click/Drag to create cropped image</div>}>
+ <button className="antimodeMenu-button annotate" onPointerDown={this.cropDown} style={{ cursor: 'grab' }}>
+ <FontAwesomeIcon icon="image" size="lg" />
+ </button>
+ </Tooltip>
+ )}
+ </>
+ ) : (
+ <>
+ <Tooltip key="trash" title={<div className="dash-tooltip">Remove Link Anchor</div>}>
+ <button className="antimodeMenu-button" onPointerDown={this.Delete}>
+ <FontAwesomeIcon icon="trash-alt" size="lg" />
+ </button>
+ </Tooltip>
+ <Tooltip key="Pin" title={<div className="dash-tooltip">Pin to Presentation</div>}>
+ <button className="antimodeMenu-button" onPointerDown={this.PinToPres}>
+ <FontAwesomeIcon icon="map-pin" size="lg" />
+ </button>
+ </Tooltip>
+ <Tooltip key="trail" title={<div className="dash-tooltip">Show Linked Trail</div>}>
+ <button className="antimodeMenu-button" onPointerDown={this.ShowTargetTrail}>
+ <FontAwesomeIcon icon="taxi" size="lg" />
+ </button>
+ </Tooltip>
+ <Tooltip key="toggle" title={<div className="dash-tooltip">make target visibility toggle on click</div>}>
+ <button className="antimodeMenu-button" style={{ color: this.IsTargetToggler() ? 'black' : 'white', backgroundColor: this.IsTargetToggler() ? 'white' : 'black' }} onPointerDown={this.MakeTargetToggle}>
+ <FontAwesomeIcon icon="thumbtack" size="lg" />
+ </button>
+ </Tooltip>
+ </>
+ );
return this.getElement(buttons);
}
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 7069ff399..3b101a0c6 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -4,12 +4,13 @@ import { observer } from 'mobx-react';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
-import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
import { LinkFollower } from '../../util/LinkFollower';
import { undoBatch } from '../../util/UndoManager';
import { FieldViewProps } from '../nodes/FieldView';
import { AnchorMenu } from './AnchorMenu';
import './Annotation.scss';
+import { OpenWhere } from '../nodes/DocumentView';
interface IAnnotationProps extends FieldViewProps {
anno: Doc;
@@ -55,9 +56,17 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
pinToPres = () => this.props.pinToPres(this.annoTextRegion, {});
@undoBatch
- makePushpin = () => (this.annoTextRegion.followLinkToggle = !this.annoTextRegion.followLinkToggle);
+ makeTargretToggle = () => (this.annoTextRegion.followLinkToggle = !this.annoTextRegion.followLinkToggle);
- isPushpin = () => BoolCast(this.annoTextRegion.followLinkToggle);
+ isTargetToggler = () => BoolCast(this.annoTextRegion.followLinkToggle);
+ @undoBatch
+ showTargetTrail = (anchor: Doc) => {
+ const trail = DocCast(anchor.presTrail);
+ if (trail) {
+ Doc.ActivePresentation = trail;
+ this.props.addDocTab(trail, OpenWhere.replaceRight);
+ }
+ };
@action
onPointerDown = (e: React.PointerEvent) => {
@@ -65,10 +74,10 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
AnchorMenu.Instance.Status = 'annotation';
AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this);
AnchorMenu.Instance.Pinned = false;
- AnchorMenu.Instance.AddTag = this.addTag.bind(this);
AnchorMenu.Instance.PinToPres = this.pinToPres;
- AnchorMenu.Instance.MakePushpin = this.makePushpin;
- AnchorMenu.Instance.IsPushpin = this.isPushpin;
+ AnchorMenu.Instance.MakeTargetToggle = this.makeTargretToggle;
+ AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler;
+ AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this.annoTextRegion);
AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
e.stopPropagation();
} else if (e.button === 0) {
@@ -77,12 +86,6 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
}
};
- addTag = (key: string, value: string): boolean => {
- const valNum = parseInt(value);
- this.annoTextRegion[key] = isNaN(valNum) ? value : valNum;
- return true;
- };
-
render() {
const brushed = this.annoTextRegion && Doc.isBrushedHighlightedDegree(this.annoTextRegion);
return (
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index f95d5ac2e..b0b7816b8 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -135,7 +135,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
copy = (e: ClipboardEvent) => {
if (this.props.isContentActive() && e.clipboardData) {
e.clipboardData.setData('text/plain', this._selectionText);
- const anchor = this._getAnchor();
+ const anchor = this._getAnchor(undefined, false);
if (anchor) {
anchor.textCopied = true;
e.clipboardData.setData('dash/pdfAnchor', anchor[Id]);
@@ -317,7 +317,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
this._ignoreScroll = false;
if (this._scrollTimer) clearTimeout(this._scrollTimer); // wait until a scrolling pause, then create an anchor to audio
this._scrollTimer = setTimeout(() => {
- DocUtils.MakeLinkToActiveAudio(() => this.props.DocumentView?.().ComponentView?.getAnchor!()!, false);
+ DocUtils.MakeLinkToActiveAudio(() => this.props.DocumentView?.().ComponentView?.getAnchor!(true)!, false);
this._scrollTimer = undefined;
}, 200);
}
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index aac488559..4c4275ce7 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -2,7 +2,7 @@ import { Tooltip } from '@material-ui/core';
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field } from '../../../fields/Doc';
+import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { DocCast, StrCast } from '../../../fields/Types';
import { StopEvent } from '../../../Utils';
@@ -10,6 +10,7 @@ import { DocUtils } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
import { DocumentManager } from '../../util/DocumentManager';
import { LinkManager } from '../../util/LinkManager';
+import { undoBatch } from '../../util/UndoManager';
import { CollectionDockingView } from '../collections/CollectionDockingView';
import { ViewBoxBaseComponent } from '../DocComponent';
import { FieldView, FieldViewProps } from '../nodes/FieldView';
@@ -113,14 +114,12 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
this.selectElement(doc, () => DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, false));
});
- // TODO: nda -- Change this method to change what happens when you click on the item.
+ @undoBatch
makeLink = action((linkTo: Doc) => {
- if (this.props.linkCreateAnchor) {
- const linkFrom = this.props.linkCreateAnchor();
- if (linkFrom) {
- const link = DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo });
- link && this.props.linkCreated?.(link);
- }
+ const linkFrom = this.props.linkCreateAnchor?.();
+ if (linkFrom) {
+ const link = DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo });
+ link && this.props.linkCreated?.(link);
}
});
@@ -206,6 +205,13 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
*/
@action
searchCollection(query: string) {
+ this._selectedResult = undefined;
+ this._results = SearchBox.staticSearchCollection(CollectionDockingView.Instance?.rootDoc, query);
+
+ this.computePageRanks();
+ }
+ @action
+ static staticSearchCollection(rootDoc: Opt<Doc>, query: string) {
const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
const blockedKeys = [
'x',
@@ -241,18 +247,15 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
'panY',
'viewScale',
];
- const collection = CollectionDockingView.Instance;
query = query.toLowerCase();
- this._results.clear();
- this._selectedResult = undefined;
-
- if (collection !== undefined) {
- const docs = DocListCast(collection.rootDoc[Doc.LayoutFieldKey(collection.rootDoc)]);
+ const results = new Map<Doc, string[]>();
+ if (rootDoc) {
+ const docs = DocListCast(rootDoc[Doc.LayoutFieldKey(rootDoc)]);
const docIDs: String[] = [];
SearchBox.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => {
- const dtype = StrCast(doc.type, 'string') as DocumentType;
- if (dtype && !blockedTypes.includes(dtype) && !docIDs.includes(doc[Id]) && depth > 0) {
+ const dtype = StrCast(doc.type) as DocumentType;
+ if (dtype && !blockedTypes.includes(dtype) && !docIDs.includes(doc[Id]) && depth >= 0) {
const hlights = new Set<string>();
SearchBox.documentKeys(doc).forEach(
key =>
@@ -263,14 +266,13 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
blockedKeys.forEach(key => hlights.delete(key));
if (Array.from(hlights.keys()).length > 0) {
- this._results.set(doc, Array.from(hlights.keys()));
+ results.set(doc, Array.from(hlights.keys()));
}
}
docIDs.push(doc[Id]);
});
}
-
- this.computePageRanks();
+ return results;
}
/**
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 19ffc5005..df49c32f0 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -239,6 +239,19 @@ export class Doc extends RefField {
public static get MyFileOrphans() {
return DocCast(Doc.UserDoc().myFileOrphans);
}
+ public static AddFileOrphan(doc: Doc) {
+ if (
+ doc &&
+ Doc.MyFileOrphans instanceof Doc &&
+ Doc.IsPrototype(doc) &&
+ !Doc.IsSystem(doc) &&
+ ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(doc.type as any) &&
+ !doc.isFolder &&
+ !doc.annotationOn
+ ) {
+ Doc.AddDocToList(Doc.MyFileOrphans, undefined, doc);
+ }
+ }
public static get MyTools() {
return DocCast(Doc.UserDoc().myTools);
}
@@ -774,7 +787,7 @@ export namespace Doc {
}
cloneMap.set(doc[Id], copy);
}
- Doc.IsPrototype(copy) && Doc.AddDocToList(Doc.MyFileOrphans, undefined, copy);
+ Doc.AddFileOrphan(copy);
return copy;
}
export async function MakeClone(doc: Doc, dontCreate: boolean = false, asBranch = false, cloneMap: Map<string, Doc> = new Map()) {
@@ -958,7 +971,7 @@ export namespace Doc {
Object.keys(doc).forEach(key => {
if (exclude.includes(key)) return;
const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
- const field = ProxyField.WithoutProxy(() => doc[key]);
+ const field = key === 'author' ? Doc.CurrentUserEmail : ProxyField.WithoutProxy(() => doc[key]);
if (key === 'proto' && copyProto) {
if (doc[key] instanceof Doc) {
copy[key] = Doc.MakeCopy(doc[key]!, false);
@@ -982,7 +995,6 @@ export namespace Doc {
}
}
});
- copy.author = Doc.CurrentUserEmail;
if (copyProto) {
Doc.GetProto(copy).context = undefined;
Doc.GetProto(copy).aliases = new List<Doc>([copy]);
@@ -994,7 +1006,7 @@ export namespace Doc {
if (retitle) {
copy.title = incrementTitleCopy(StrCast(copy.title));
}
- Doc.IsPrototype(copy) && Doc.AddDocToList(Doc.MyFileOrphans, undefined, copy);
+ Doc.AddFileOrphan(copy);
return copy;
}
@@ -1013,6 +1025,7 @@ export namespace Doc {
if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DataSym], 'aliases', delegate);
title && (delegate.title = title);
delegate[Initializing] = false;
+ Doc.AddFileOrphan(delegate);
return delegate;
}
return undefined;
diff --git a/src/fields/FieldLoader.scss b/src/fields/FieldLoader.scss
index 123488c7d..9a23c3e4f 100644
--- a/src/fields/FieldLoader.scss
+++ b/src/fields/FieldLoader.scss
@@ -1,12 +1,15 @@
.fieldLoader {
z-index: 10000;
width: 200px;
- height: 50;
- background: white;
+ height: 30;
+ background: lightblue;
position: absolute;
left: calc(50% - 99px);
- top: calc(50% + 99px);
+ top: calc(50% + 110px);
display: flex;
align-items: center;
padding: 20px;
+ margin: auto;
+ display: 'block';
+ box-shadow: darkslategrey 0.2vw 0.2vw 0.8vw;
}
diff --git a/src/fields/FieldLoader.tsx b/src/fields/FieldLoader.tsx
index 36dca89f2..2a7b936f7 100644
--- a/src/fields/FieldLoader.tsx
+++ b/src/fields/FieldLoader.tsx
@@ -10,18 +10,6 @@ export class FieldLoader extends React.Component {
public static active = false;
render() {
- return (
- <div
- className="fieldLoader"
- style={{
- zIndex: 10000,
- margin: 'auto',
- width: 200,
- height: 75,
- background: 'lightblue',
- display: 'block',
- position: 'absolute',
- }}>{`Requested: ${FieldLoader.ServerLoadStatus.requested} ... ${FieldLoader.ServerLoadStatus.retrieved} `}</div>
- );
+ return <div className="fieldLoader">{`Requested: ${FieldLoader.ServerLoadStatus.requested} ... ${FieldLoader.ServerLoadStatus.retrieved} `}</div>;
}
}
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index bf40a0d7b..3ef7cb1de 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -1,48 +1,50 @@
-import { Field, Opt, FieldResult, Doc } from "./Doc";
-import { List } from "./List";
-import { RefField } from "./RefField";
-import { DateField } from "./DateField";
-import { ScriptField } from "./ScriptField";
-import { URLField, WebField, ImageField } from "./URLField";
-import { TextField } from "@material-ui/core";
-import { RichTextField } from "./RichTextField";
-
-export type ToType<T extends InterfaceValue> =
- T extends "string" ? string :
- T extends "number" ? number :
- T extends "boolean" ? boolean :
- T extends ListSpec<infer U> ? List<U> :
- // T extends { new(...args: any[]): infer R } ? (R | Promise<R>) : never;
- T extends DefaultFieldConstructor<infer _U> ? never :
- T extends { new(...args: any[]): List<Field> } ? never :
- T extends { new(...args: any[]): infer R } ? R :
- T extends (doc?: Doc) => infer R ? R : never;
-
-export type ToConstructor<T extends Field> =
- T extends string ? "string" :
- T extends number ? "number" :
- T extends boolean ? "boolean" :
- T extends List<infer U> ? ListSpec<U> :
- new (...args: any[]) => T;
+import { Field, Opt, FieldResult, Doc } from './Doc';
+import { List } from './List';
+import { RefField } from './RefField';
+import { DateField } from './DateField';
+import { ScriptField } from './ScriptField';
+import { URLField, WebField, ImageField } from './URLField';
+import { TextField } from '@material-ui/core';
+import { RichTextField } from './RichTextField';
+
+export type ToType<T extends InterfaceValue> = T extends 'string'
+ ? string
+ : T extends 'number'
+ ? number
+ : T extends 'boolean'
+ ? boolean
+ : T extends ListSpec<infer U>
+ ? List<U>
+ : // T extends { new(...args: any[]): infer R } ? (R | Promise<R>) : never;
+ T extends DefaultFieldConstructor<infer _U>
+ ? never
+ : T extends { new (...args: any[]): List<Field> }
+ ? never
+ : T extends { new (...args: any[]): infer R }
+ ? R
+ : T extends (doc?: Doc) => infer R
+ ? R
+ : never;
+
+export type ToConstructor<T extends Field> = T extends string ? 'string' : T extends number ? 'number' : T extends boolean ? 'boolean' : T extends List<infer U> ? ListSpec<U> : new (...args: any[]) => T;
export type ToInterface<T extends Interface> = {
- [P in Exclude<keyof T, "proto">]: T[P] extends DefaultFieldConstructor<infer F> ? Exclude<FieldResult<F>, undefined> : FieldResult<ToType<T[P]>>;
+ [P in Exclude<keyof T, 'proto'>]: T[P] extends DefaultFieldConstructor<infer F> ? Exclude<FieldResult<F>, undefined> : FieldResult<ToType<T[P]>>;
};
// type ListSpec<T extends Field[]> = { List: ToContructor<Head<T>> | ListSpec<Tail<T>> };
export type ListSpec<T extends Field> = { List: ToConstructor<T> };
export type DefaultFieldConstructor<T extends Field> = {
- type: ToConstructor<T>,
- defaultVal: T
+ type: ToConstructor<T>;
+ defaultVal: T;
};
// type ListType<U extends Field[]> = { 0: List<ListType<Tail<U>>>, 1: ToType<Head<U>> }[HasTail<U> extends true ? 0 : 1];
export type Head<T extends any[]> = T extends [any, ...any[]] ? T[0] : never;
-export type Tail<T extends any[]> =
- ((...t: T) => any) extends ((_: any, ...tail: infer TT) => any) ? TT : [];
-export type HasTail<T extends any[]> = T extends ([] | [any]) ? false : true;
+export type Tail<T extends any[]> = ((...t: T) => any) extends (_: any, ...tail: infer TT) => any ? TT : [];
+export type HasTail<T extends any[]> = T extends [] | [any] ? false : true;
export type InterfaceValue = ToConstructor<Field> | ListSpec<Field> | DefaultFieldConstructor<Field> | ((doc?: Doc) => any);
//TODO Allow you to optionally specify default values for schemas, which should then make that field not be partial
@@ -58,14 +60,14 @@ export function Cast<T extends CastCtor>(field: FieldResult, ctor: T): FieldResu
export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal: WithoutList<WithoutRefField<ToType<T>>> | null): WithoutList<ToType<T>>;
export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal?: ToType<T> | null): FieldResult<ToType<T>> | undefined {
if (field instanceof Promise) {
- return defaultVal === undefined ? field.then(f => Cast(f, ctor) as any) as any : defaultVal === null ? undefined : defaultVal;
+ return defaultVal === undefined ? (field.then(f => Cast(f, ctor) as any) as any) : defaultVal === null ? undefined : defaultVal;
}
if (field !== undefined && !(field instanceof Promise)) {
- if (typeof ctor === "string") {
+ if (typeof ctor === 'string') {
if (typeof field === ctor) {
return field as ToType<T>;
}
- } else if (typeof ctor === "object") {
+ } else if (typeof ctor === 'object') {
if (field instanceof List) {
return field as any;
}
@@ -81,15 +83,15 @@ export function DocCast(field: FieldResult, defaultVal?: Doc) {
}
export function NumCast(field: FieldResult, defaultVal: number | null = 0) {
- return Cast(field, "number", defaultVal);
+ return Cast(field, 'number', defaultVal);
}
-export function StrCast(field: FieldResult, defaultVal: string | null = "") {
- return Cast(field, "string", defaultVal);
+export function StrCast(field: FieldResult, defaultVal: string | null = '') {
+ return Cast(field, 'string', defaultVal);
}
export function BoolCast(field: FieldResult, defaultVal: boolean | null = false) {
- return Cast(field, "boolean", defaultVal);
+ return Cast(field, 'boolean', defaultVal);
}
export function DateCast(field: FieldResult) {
return Cast(field, DateField, null);
@@ -113,7 +115,7 @@ type WithoutList<T extends Field> = T extends List<infer R> ? (R extends RefFiel
export function FieldValue<T extends Field, U extends WithoutList<T>>(field: FieldResult<T>, defaultValue: U): WithoutList<T>;
export function FieldValue<T extends Field>(field: FieldResult<T>): Opt<T>;
export function FieldValue<T extends Field>(field: FieldResult<T>, defaultValue?: T): Opt<T> {
- return (field instanceof Promise || field === undefined) ? defaultValue : field;
+ return field instanceof Promise || field === undefined ? defaultValue : field;
}
export interface PromiseLike<T> {
@@ -121,5 +123,9 @@ export interface PromiseLike<T> {
}
export function PromiseValue<T extends Field>(field: FieldResult<T>): PromiseLike<Opt<T>> {
if (field instanceof Promise) return field as Promise<Opt<T>>;
- return { then(cb: ((field: Opt<T>) => void)) { return cb(field); } };
-} \ No newline at end of file
+ return {
+ then(cb: (field: Opt<T>) => void) {
+ return cb(field);
+ },
+ };
+}