aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/DocumentView.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/DocumentView.tsx')
-rw-r--r--src/client/views/nodes/DocumentView.tsx180
1 files changed, 115 insertions, 65 deletions
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index dea718a0d..f9ef85595 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -10,7 +10,7 @@ import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
import { listSpec } from '../../../fields/Schema';
import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { AudioField } from '../../../fields/URLField';
import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
import { MobileInterface } from '../../../mobile/MobileInterface';
@@ -52,6 +52,8 @@ import { RadialMenu } from './RadialMenu';
import { ScriptingBox } from './ScriptingBox';
import { PresBox } from './trails/PresBox';
import React = require('react');
+import { DictationManager } from '../../util/DictationManager';
+import { Tooltip } from '@material-ui/core';
const { Howl } = require('howler');
interface Window {
@@ -156,6 +158,7 @@ export interface DocumentViewSharedProps {
scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
createNewFilterDoc?: () => void;
updateFilterDoc?: (doc: Doc) => void;
+ dontHideOnDrag?: boolean;
}
// these props are specific to DocuentViews
@@ -175,9 +178,9 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
LayoutTemplateString?: string;
dontCenter?: 'x' | 'y' | 'xy';
dontScaleFilter?: (doc: Doc) => boolean; // decides whether a document can be scaled to fit its container vs native size with scrolling
- ContentScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal
NativeWidth?: () => number;
NativeHeight?: () => number;
+ NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal NOTE: Must also be added to FieldViewProps
LayoutTemplate?: () => Opt<Doc>;
contextMenuItems?: () => { script: ScriptField; filter?: ScriptField; label: string; icon: string }[];
onClick?: () => ScriptField;
@@ -203,7 +206,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
public static SelectAfterContextMenu = true; // whether a document should be selected after it's contextmenu is triggered.
_animateScaleTime = 300; // milliseconds;
@observable _animateScalingTo = 0;
- @observable _mediaState = 0;
@observable _pendingDoubleClick = false;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _downX: number = 0;
@@ -235,11 +237,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@computed get ShowTitle() {
return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as Opt<string>;
}
- @computed get ContentScale() {
- return this.props.ContentScaling?.() || 1;
+ @computed get NativeDimScaling() {
+ return this.props.NativeDimScaling?.() || 1;
}
@computed get thumb() {
- return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url.href.replace('.png', '_m.png');
+ return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url?.href.replace('.png', '_m.png');
}
@computed get hidden() {
return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Hidden);
@@ -428,7 +430,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
let nheight = Doc.NativeHeight(layoutDoc);
const width = layoutDoc._width || 0;
const height = layoutDoc._height || (nheight / nwidth) * width;
- const scale = this.props.ScreenToLocalTransform().Scale * this.ContentScale;
+ const scale = this.props.ScreenToLocalTransform().Scale * this.NativeDimScaling;
const actualdW = Math.max(width + dW * scale, 20);
const actualdH = Math.max(height + dH * scale, 20);
doc.x = (doc.x || 0) + dX * (actualdW - width);
@@ -491,10 +493,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
startDragging(x: number, y: number, dropAction: dropActionType, hideSource = false) {
if (this._mainCont.current) {
const dragData = new DragManager.DocumentDragData([this.props.Document]);
- const [left, top] = this.props.ScreenToLocalTransform().scale(this.ContentScale).inverse().transformPoint(0, 0);
+ const [left, top] = this.props.ScreenToLocalTransform().scale(this.NativeDimScaling).inverse().transformPoint(0, 0);
dragData.offset = this.props
.ScreenToLocalTransform()
- .scale(this.ContentScale)
+ .scale(this.NativeDimScaling)
.transformDirection(x - left, y - top);
dragData.offset[0] = Math.min(this.rootDoc[WidthSym](), dragData.offset[0]);
dragData.offset[1] = Math.min(this.rootDoc[HeightSym](), dragData.offset[1]);
@@ -502,9 +504,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
dragData.treeViewDoc = this.props.treeViewDoc;
dragData.removeDocument = this.props.removeDocument;
dragData.moveDocument = this.props.moveDocument;
+ //dragData.dimSource :
+ // dragEffects field, set dim
+ // add kv pairs to a doc, swap properties with the node while dragging, and then swap when dropping
+ // add a dragEffects prop to DocumentView as a function that sets up. Each view has its own prop, when you start dragging:
+ // in Draganager, figure out which doc(s) you're dragging and change what opacity function returns
const ffview = this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
ffview && runInAction(() => (ffview.ChildDrag = this.props.DocumentView()));
- DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart) }, () => setTimeout(action(() => ffview && (ffview.ChildDrag = undefined)))); // this needs to happen after the drop event is processed.
+ DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart && !this.props.dontHideOnDrag) }, () =>
+ setTimeout(action(() => ffview && (ffview.ChildDrag = undefined)))
+ ); // this needs to happen after the drop event is processed.
ffview?.setupDragLines(false);
}
}
@@ -716,7 +725,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
this.Document.followLinkLocation = location;
} else if (this.Document._isLinkButton && this.onClickHandler) {
this.Document._isLinkButton = false;
- this.Document['onClick-rawScript'] = this.dataDoc['onClick-rawScript'] = this.dataDoc.onClick = this.Document.onClick = this.layoutDoc.onClick = undefined;
+ this.dataDoc.onClick = this.Document.onClick = this.layoutDoc.onClick = undefined;
}
};
@undoBatch
@@ -750,7 +759,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
};
@undoBatch deleteClicked = () => this.props.removeDocument?.(this.props.Document);
- @undoBatch setToggleDetail = () => (this.Document.onClick = ScriptField.MakeScript(`toggleDetail(documentView, "${StrCast(this.Document.layoutKey).replace('layout_', '')}")`, { documentView: 'any' }));
+ @undoBatch setToggleDetail = () =>
+ (this.Document.onClick = ScriptField.MakeScript(
+ `toggleDetail(documentView, "${StrCast(this.Document.layoutKey)
+ .replace('layout_', '')
+ .replace(/^layout$/, 'detail')}")`,
+ { documentView: 'any' }
+ ));
@undoBatch
@action
@@ -863,7 +878,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
!appearance && cm.addItem({ description: 'UI Controls...', subitems: appearanceItems, icon: 'compass' });
if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
- !Doc.noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? 'Show' : 'Hide'} Audio Button`, event: action(() => (this.layoutDoc._showAudio = !this.layoutDoc._showAudio)), icon: 'microphone' });
const existingOnClick = cm.findByDescription('OnClick...');
const onClicks: ContextMenuProps[] = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
@@ -961,7 +975,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
panelHeight = () => this.props.PanelHeight() - this.headerMargin;
screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
- contentScaling = () => this.ContentScale;
onClickFunc = () => this.onClickHandler;
setHeight = (height: number) => (this.layoutDoc._height = height);
setContentView = action((view: { getAnchor?: () => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
@@ -989,18 +1002,24 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
? true
: false;
};
+ linkButtonInverseScaling = () => (this.props.NativeDimScaling?.() || 1) * this.props.DocumentView().screenToLocalTransform().Scale;
@computed get contents() {
TraceMobx();
- const audioView = !this.layoutDoc._showAudio ? null : (
- <div className="documentView-audioBackground" onPointerDown={this.recordAudioAnnotation} onPointerEnter={this.onPointerEnter}>
- <FontAwesomeIcon
- className="documentView-audioFont"
- style={{ color: [DocListCast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations']).length ? 'blue' : 'gray', 'green', 'red'][this._mediaState] }}
- icon={!DocListCast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations']).length ? 'microphone' : 'file-audio'}
- size="sm"
- />
- </div>
- );
+ const audioAnnosCount = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations'], listSpec(AudioField), null)?.length;
+ const audioTextAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations-text'], listSpec('string'), null);
+ const audioView =
+ (!this.props.isSelected() && !this._isHovering && this.dataDoc.audioAnnoState !== 2) || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() || (!audioAnnosCount && !this.dataDoc.audioAnnoState) ? null : (
+ <Tooltip title={<div>{audioTextAnnos?.lastElement()}</div>}>
+ <div className="documentView-audioBackground" onPointerDown={this.playAnnotation}>
+ <FontAwesomeIcon
+ className="documentView-audioFont"
+ style={{ color: [audioAnnosCount ? 'blue' : 'gray', 'green', 'red'][NumCast(this.dataDoc.audioAnnoState)] }}
+ icon={!audioAnnosCount ? 'microphone' : 'file-audio'}
+ size="sm"
+ />
+ </div>
+ </Tooltip>
+ );
return (
<div
@@ -1041,7 +1060,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
thumbShown={this.thumbShown}
isHovering={this.isHovering}
setContentView={this.setContentView}
- scaling={this.contentScaling}
+ NativeDimScaling={this.props.NativeDimScaling}
PanelHeight={this.panelHeight}
setHeight={!this.props.suppressSetHeight ? this.setHeight : undefined}
isContentActive={this.isContentActive}
@@ -1055,8 +1074,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
{(!this.props.isSelected() && !this._isHovering) || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? null : (
<DocumentLinksButton
View={this.props.DocumentView()}
- ContentScaling={this.props.ContentScaling}
- Offset={[this.topMost ? 0 : !this.props.isSelected() ? -15 : -30, undefined, undefined, this.topMost ? 10 : !this.props.isSelected() ? -15 : -30]}
+ scaling={this.linkButtonInverseScaling}
+ Offset={[this.topMost ? 0 : !this.props.isSelected() ? -15 : -36, undefined, undefined, this.topMost ? 10 : !this.props.isSelected() ? -15 : -28]}
/>
)}
{audioView}
@@ -1134,58 +1153,72 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
@action
- onPointerEnter = () => {
+ playAnnotation = () => {
const self = this;
- const audioAnnos = DocListCast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations']);
- if (audioAnnos && audioAnnos.length && this._mediaState === 0) {
- const anno = audioAnnos[Math.floor(Math.random() * audioAnnos.length)];
- anno.data instanceof AudioField &&
- new Howl({
- src: [anno.data.url.href],
- format: ['mp3'],
- autoplay: true,
- loop: false,
- volume: 0.5,
- onend: function () {
- runInAction(() => (self._mediaState = 0));
- },
- });
- this._mediaState = 1;
+ const audioAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations'], listSpec(AudioField), null);
+ const anno = audioAnnos.lastElement();
+ if (anno instanceof AudioField && this.dataDoc.audioAnnoState === 0) {
+ new Howl({
+ src: [anno.url.href],
+ format: ['mp3'],
+ autoplay: true,
+ loop: false,
+ volume: 0.5,
+ onend: function () {
+ runInAction(() => {
+ self.dataDoc.audioAnnoState = 0;
+ });
+ },
+ });
+ this.dataDoc.audioAnnoState = 1;
}
};
- recordAudioAnnotation = () => {
+
+ static recordAudioAnnotation(dataDoc: Doc, field: string, onEnd?: () => void) {
let gumStream: any;
let recorder: any;
- const self = this;
navigator.mediaDevices
.getUserMedia({
audio: true,
})
.then(function (stream) {
+ let audioTextAnnos = Cast(dataDoc[field + '-audioAnnotations-text'], listSpec('string'), null);
+ if (audioTextAnnos) audioTextAnnos.push('');
+ else audioTextAnnos = dataDoc[field + '-audioAnnotations-text'] = new List<string>(['']);
+ DictationManager.Controls.listen({
+ interimHandler: value => (audioTextAnnos[audioTextAnnos.length - 1] = value),
+ continuous: { indefinite: false },
+ }).then(results => {
+ if (results && [DictationManager.Controls.Infringed].includes(results)) {
+ DictationManager.Controls.stop();
+ }
+ onEnd?.();
+ });
+
gumStream = stream;
recorder = new MediaRecorder(stream);
recorder.ondataavailable = async (e: any) => {
const [{ result }] = await Networking.UploadFilesToServer(e.data);
if (!(result instanceof Error)) {
- const audioDoc = Docs.Create.AudioDocument(result.accessPaths.agnostic.client, { title: 'audio test', _width: 200, _height: 32 });
- audioDoc.treeViewExpandedView = 'layout';
- const audioAnnos = Cast(self.dataDoc[self.LayoutFieldKey + '-audioAnnotations'], listSpec(Doc));
+ const audioField = new AudioField(result.accessPaths.agnostic.client);
+ const audioAnnos = Cast(dataDoc[field + '-audioAnnotations'], listSpec(AudioField), null);
if (audioAnnos === undefined) {
- self.dataDoc[self.LayoutFieldKey + '-audioAnnotations'] = new List([audioDoc]);
+ dataDoc[field + '-audioAnnotations'] = new List([audioField]);
} else {
- audioAnnos.push(audioDoc);
+ audioAnnos.push(audioField);
}
}
};
- runInAction(() => (self._mediaState = 2));
+ runInAction(() => (dataDoc.audioAnnoState = 2));
recorder.start();
setTimeout(() => {
recorder.stop();
- runInAction(() => (self._mediaState = 0));
+ DictationManager.Controls.stop(false);
+ runInAction(() => (dataDoc.audioAnnoState = 0));
gumStream.getAudioTracks()[0].stop();
}, 5000);
});
- };
+ }
captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ':caption');
@computed get innards() {
@@ -1229,6 +1262,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
style={{
position: this.headerMargin ? 'relative' : 'absolute',
height: this.titleHeight,
+ width: !this.headerMargin ? `calc(100% - 18px)` : '100%', // leave room for annotation button
color: lightOrDark(background),
background,
pointerEvents: this.onClickHandler || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
@@ -1283,9 +1317,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
isHovering = () => this._isHovering;
@observable _isHovering = false;
@observable _: string = '';
+ _hoverTimeout: any = undefined;
@computed get renderDoc() {
TraceMobx();
- const thumb = ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url.href.replace('.png', '_m.png');
+ const thumb = ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url?.href.replace('.png', '_m.png');
const isButton = this.props.Document.type === DocumentType.FONTICON;
if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || (this.hidden && !this.props.treeViewDoc)) return null;
return (
@@ -1293,8 +1328,17 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
<div
className={`documentView-node${this.topMost ? '-topmost' : ''}`}
id={this.props.Document[Id]}
- onPointerEnter={action(() => (this._isHovering = true))}
- onPointerLeave={action(() => (this._isHovering = false))}
+ onPointerEnter={action(() => {
+ clearTimeout(this._hoverTimeout);
+ this._isHovering = true;
+ })}
+ onPointerLeave={action(() => {
+ clearTimeout(this._hoverTimeout);
+ this._hoverTimeout = setTimeout(
+ action(() => (this._isHovering = false)),
+ 500
+ );
+ })}
style={{
background: isButton || thumb ? undefined : this.backgroundColor,
opacity: this.opacity,
@@ -1492,7 +1536,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return this.effectiveNativeWidth ? (this.props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2 : 0;
}
@computed get Yshift() {
- return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 && !this.layoutDoc.nativeHeightUnfrozen ? Math.max(0, (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2) : 0;
+ return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 && (!this.layoutDoc.nativeHeightUnfrozen || (!this.fitWidth && this.effectiveNativeHeight * this.nativeScaling <= this.props.PanelHeight()))
+ ? Math.max(0, (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2)
+ : 0;
}
@computed get centeringX() {
return this.props.dontCenter?.includes('x') ? 0 : this.Xshift;
@@ -1501,7 +1547,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return this.props.dontCenter?.includes('y') ? 0 : this.Yshift;
}
- toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.ContentScale, this.props.PanelWidth(), this.props.PanelHeight());
+ toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight());
focus = (doc: Doc, options?: DocFocusOptions) => this.docView?.focus(doc, options);
getBounds = () => {
if (!this.docView || !this.docView.ContentDiv || this.props.Document.presBox || this.docView.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
@@ -1535,11 +1581,15 @@ export class DocumentView extends React.Component<DocumentViewProps> {
Doc.setNativeView(this.props.Document);
custom && DocUtils.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined);
};
- switchViews = action((custom: boolean, view: string, finished?: () => void) => {
+ switchViews = action((custom: boolean, view: string, finished?: () => void, useExistingLayout = false) => {
this.docView && (this.docView._animateScalingTo = 0.1); // shrink doc
setTimeout(
action(() => {
- this.setCustomView(custom, view);
+ if (useExistingLayout && custom && this.rootDoc['layout_' + view]) {
+ this.rootDoc.layoutKey = 'layout_' + view;
+ } else {
+ this.setCustomView(custom, view);
+ }
this.docView && (this.docView._animateScalingTo = 1); // expand it
setTimeout(
action(() => {
@@ -1562,7 +1612,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
NativeHeight = () => this.effectiveNativeHeight;
PanelWidth = () => this.panelWidth;
PanelHeight = () => this.panelHeight;
- ContentScale = () => this.nativeScaling;
+ NativeDimScaling = () => this.nativeScaling;
selfView = () => this;
screenToLocalTransform = () =>
this.props
@@ -1590,8 +1640,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
render() {
TraceMobx();
- const xshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined);
- const yshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined);
+ const xshift = () => (this.props.Document.isInkMask && !this.props.LayoutTemplateString && !this.props.LayoutTemplate?.() ? InkingStroke.MaskDim : Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined);
+ const yshift = () => (this.props.Document.isInkMask && !this.props.LayoutTemplateString && !this.props.LayoutTemplate?.() ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined);
const isPresTreeElement: boolean = this.props.treeViewDoc?.type === DocumentType.PRES;
const isButton: boolean = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear;
return (
@@ -1618,9 +1668,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
PanelHeight={this.PanelHeight}
NativeWidth={this.NativeWidth}
NativeHeight={this.NativeHeight}
+ NativeDimScaling={this.NativeDimScaling}
isSelected={this.isSelected}
select={this.select}
- ContentScaling={this.ContentScale}
ScreenToLocalTransform={this.screenToLocalTransform}
focus={this.props.focus || emptyFunction}
ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))}
@@ -1643,7 +1693,7 @@ ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) {
ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
if (dv.Document.layoutKey === 'layout_' + detailLayoutKeySuffix) dv.switchViews(false, 'layout');
- else dv.switchViews(true, detailLayoutKeySuffix);
+ else dv.switchViews(true, detailLayoutKeySuffix, undefined, true);
});
ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc) {