aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/util/DocumentManager.ts26
-rw-r--r--src/client/views/AntimodeMenu.tsx143
-rw-r--r--src/client/views/DocumentButtonBar.tsx30
-rw-r--r--src/client/views/MainView.tsx1
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx60
-rw-r--r--src/client/views/nodes/DocumentView.tsx4
-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.tsx12
-rw-r--r--src/client/views/nodes/formattedText/marks_rts.ts2
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx307
-rw-r--r--src/client/views/nodes/trails/PresElementBox.tsx10
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx145
-rw-r--r--src/client/views/pdf/Annotation.tsx7
-rw-r--r--src/client/views/search/SearchBox.tsx13
15 files changed, 400 insertions, 368 deletions
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 70fe7f2c0..aa09fb005 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,16 @@ export class DocumentManager {
return toReturn;
}
+ static GetContextPath(doc: Opt<Doc>) {
+ 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 && !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/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/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index f06dc93e3..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;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 4494166f2..839db5795 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,
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/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 054d01d8e..b94db2c6b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -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;
}
};
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 deb1d68a7..8407eee96 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -693,6 +693,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
@undoBatch
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);
+ }
+ };
+
isTargetToggler = (anchor: Doc) => BoolCast(anchor.followLinkToggle);
specificContextMenu = (e: React.MouseEvent): void => {
@@ -717,6 +726,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
AnchorMenu.Instance.Pinned = false;
AnchorMenu.Instance.PinToPres = () => this.pinToPres(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);
})
@@ -1471,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/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.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 226fd8d7d..4a60c99ec 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -129,8 +129,8 @@ 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
@@ -211,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 =
@@ -224,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 () => {
@@ -232,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)();
}
@@ -513,24 +508,11 @@ 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 = () => (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 selViewCache = Array.from(this.selectedArray);
const dragViewCache = Array.from(this._dragArray);
const eleViewCache = Array.from(this._eleArray);
const resetSelection = action(() => {
- if (!includesDoc()) {
+ if (!this.props.isSelected()) {
const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc);
if (presDocView) SelectionManager.SelectView(presDocView, false);
this.clearSelectedArray();
@@ -542,17 +524,28 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
});
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.
@@ -562,47 +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) {
- 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),
- };
- if (activeItem.presentationTargetDoc instanceof Doc) activeItem.presentationTargetDoc[AnimationSym] = undefined;
- var containerDocContext = srcContext ? [srcContext] : [];
- while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) {
- containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
- }
- const testTarget = containerDocContext.length ? containerDocContext[0] : targetDoc;
- if (LightboxView.LightboxDoc && !DocumentManager.Instance.getLightboxDocumentView(testTarget)) {
- DocumentManager.Instance.AddViewRenderedCb(LightboxView.LightboxDoc, dv => {
- if (LightboxView.LightboxDoc && !DocumentManager.Instance.getLightboxDocumentView(LightboxView.LightboxDoc)) {
- DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finished);
- restoreLayout();
- } else {
- LightboxView.SetLightboxDoc(undefined);
- DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finished);
- restoreLayout();
- }
- });
- return;
- }
- DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finished);
- restoreLayout();
- } else restoreLayout();
+ const finishAndRestoreLayout = () => {
+ finished?.();
+ restoreLayout();
+ };
+ const containerDocContext = DocumentManager.GetContextPath(targetDoc);
+
+ let context = containerDocContext.length ? containerDocContext[0] : targetDoc;
+ if (activeItem.presOpenInLightbox) {
+ if (!DocumentManager.Instance.getLightboxDocumentView(DocCast(DocCast(targetDoc.annotationOn) ?? targetDoc))) {
+ context = DocCast(targetDoc.annotationOn) ?? targetDoc;
+ LightboxView.SetLightboxDoc(context); // openInTab(targetDoc);
+ }
}
+ 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();
}
/**
@@ -615,38 +590,34 @@ 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;
}
}
});
@@ -894,8 +865,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
@@ -1043,7 +1015,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))
@@ -1209,8 +1181,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
@@ -1255,8 +1227,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}
@@ -1309,11 +1279,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' }}>
@@ -1355,29 +1325,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>
@@ -1412,48 +1378,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.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 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
@@ -1658,7 +1622,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()}
@@ -1794,7 +1758,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) {
@@ -2300,12 +2266,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 f1c97d26a..788900b46 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -365,8 +365,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
// 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;
};
@@ -381,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 2fb795b06..9af686d83 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -11,13 +11,13 @@ import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu';
import { LinkPopup } from '../linking/LinkPopup';
import { ButtonDropdown } from '../nodes/formattedText/RichTextMenu';
import './AnchorMenu.scss';
-import { truncate } from 'lodash';
@observer
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)',
@@ -37,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;
@@ -56,9 +53,9 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
public Highlight: (color: string, isTargetToggler: 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 MakeTargetToggle: () => void = unimplementedFunction;
+ public ShowTargetTrail: () => void = unimplementedFunction;
public IsTargetToggler: () => boolean = returnFalse;
public get Active() {
return this._left > 0;
@@ -71,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 => {
@@ -175,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="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>,
- // <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 d8e44ae9d..0a8c69881 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -65,7 +65,6 @@ 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.MakeTargetToggle = this.makeTargretToggle;
AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler;
@@ -77,12 +76,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/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index aac488559..7e2a5a237 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -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);
}
});