aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/DocumentButtonBar.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/DocumentButtonBar.tsx')
-rw-r--r--src/client/views/DocumentButtonBar.tsx301
1 files changed, 221 insertions, 80 deletions
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index ce77b7446..35b0b22a8 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -5,25 +5,27 @@ import { action, computed, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { Doc } from '../../fields/Doc';
import { RichTextField } from '../../fields/RichTextField';
-import { Cast, NumCast } from '../../fields/Types';
-import { emptyFunction, setupMoveUpEvents, simulateMouseClick } from '../../Utils';
+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 { IsFollowLinkScript } from '../util/LinkFollower';
import { SelectionManager } from '../util/SelectionManager';
-import { SettingsManager } from '../util/SettingsManager';
import { SharingManager } from '../util/SharingManager';
-import { undoBatch } from '../util/UndoManager';
+import { undoBatch, UndoManager } from '../util/UndoManager';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { TabDocView } from './collections/TabDocView';
import './DocumentButtonBar.scss';
import { Colors } from './global/globalEnums';
+import { LinkPopup } from './linking/LinkPopup';
import { MetadataEntryMenu } from './MetadataEntryMenu';
import { DocumentLinksButton } from './nodes/DocumentLinksButton';
-import { DocumentView, DocumentViewInternal } from './nodes/DocumentView';
+import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from './nodes/DocumentView';
import { DashFieldView } from './nodes/formattedText/DashFieldView';
import { GoogleRef } from './nodes/formattedText/FormattedTextBox';
+import { PinProps } from './nodes/trails';
import { TemplateMenu } from './TemplateMenu';
import React = require('react');
const higflyout = require('@hig/flyout');
@@ -176,11 +178,11 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
e.preventDefault();
let googleDoc = await Cast(dataDoc.googleDoc, Doc);
if (!googleDoc) {
- const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, useCors: false };
+ const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, data_useCors: false };
googleDoc = Docs.Create.WebDocument(googleDocUrl, options);
dataDoc.googleDoc = googleDoc;
}
- CollectionDockingView.AddSplit(googleDoc, 'right');
+ CollectionDockingView.AddSplit(googleDoc, OpenWhereMod.right);
} else if (e.altKey) {
e.preventDefault();
window.open(googleDocUrl);
@@ -209,26 +211,128 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
</Tooltip>
);
}
+ @observable subFollow = '';
@computed
get followLinkButton() {
const targetDoc = this.view0?.props.Document;
+ const followBtn = (allDocs: boolean, click: (doc: Doc) => void, isSet: (doc?: Doc) => boolean, icon: IconProp) => {
+ const tooltip = `Follow ${this.subPin}documents`;
+ return !tooltip ? null : (
+ <Tooltip title={<div className="dash-tooltip">{tooltip}</div>}>
+ <div className="documentButtonBar-followIcon" style={{ backgroundColor: isSet(targetDoc) ? Colors.LIGHT_BLUE : Colors.DARK_GRAY, color: isSet(targetDoc) ? Colors.BLACK : Colors.WHITE }}>
+ <FontAwesomeIcon
+ className="documentdecorations-icon"
+ style={{ width: 20 }}
+ key={icon.toString()}
+ size="sm"
+ icon={icon}
+ onPointerEnter={action(e => (this.subPin = allDocs ? 'All ' : ''))}
+ onPointerLeave={action(e => (this.subPin = ''))}
+ onClick={e => {
+ this.props.views().forEach(dv => click(dv!.rootDoc));
+ e.stopPropagation();
+ }}
+ />
+ </div>
+ </Tooltip>
+ );
+ };
+ const followLink = IsFollowLinkScript(targetDoc?.onClick);
return !targetDoc ? null : (
- <Tooltip title={<div className="dash-tooltip">{'Set onClick to follow primary link'}</div>}>
+ <Tooltip title={<div className="dash-tooltip">Set onClick to follow primary link</div>}>
<div
- className="documentButtonBar-icon"
- style={{ backgroundColor: targetDoc.isLinkButton ? Colors.LIGHT_BLUE : Colors.DARK_GRAY, color: targetDoc.isLinkButton ? Colors.BLACK : Colors.WHITE }}
- onClick={undoBatch(e => this.props.views().map(view => view?.docView?.toggleFollowLink(undefined, undefined, false)))}>
+ className="documentButtonBar-icon documentButtonBar-follow"
+ style={{ backgroundColor: followLink ? Colors.LIGHT_BLUE : Colors.DARK_GRAY, color: followLink ? Colors.BLACK : Colors.WHITE }}
+ onClick={undoBatch(e => this.props.views().map(view => view?.docView?.toggleFollowLink(undefined, false)))}>
+ <div className="documentButtonBar-followTypes">
+ {followBtn(
+ true,
+ (doc: Doc) => (doc.followAllLinks = !doc.followAllLinks),
+ (doc?: Doc) => (doc?.followAllLinks ? true : false),
+ 'window-maximize'
+ )}
+ </div>
<FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="hand-point-right" />
</div>
</Tooltip>
);
}
- @observable expandPin = false;
+
+ @observable subLink = '';
+ @computed get linkButton() {
+ const targetDoc = this.view0?.props.Document;
+ return !targetDoc || !this.view0 ? null : (
+ <div className="documentButtonBar-icon documentButtonBar-link">
+ <div className="documentButtonBar-linkTypes">
+ <Tooltip title={<div>search for target</div>}>
+ <div className="documentButtonBar-button">
+ <button style={{ backgroundColor: 'transparent', width: 35, height: 35, display: 'flex', justifyContent: 'center', alignItems: 'center', position: 'relative' }} onPointerDown={this.toggleLinkSearch}>
+ <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(1.5)' }} icon={'search'} size="lg" />
+ <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(0.5)', transformOrigin: 'center', top: 9, left: 2 }} icon={'link'} size="lg" />
+ </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} />
+ </div>
+ </div>
+ );
+ }
+ @observable subEndLink = '';
+ @computed
+ get endLinkButton() {
+ const linkBtn = (pinLayout: boolean, pinContent: boolean, icon: IconProp) => {
+ const tooltip = `Finish Link and Save ${this.subEndLink} data`;
+ return !this.view0 ? null : (
+ <Tooltip title={<div className="dash-tooltip">{tooltip}</div>}>
+ <div className="documentButtonBar-pinIcon">
+ <FontAwesomeIcon
+ className="documentdecorations-icon"
+ style={{ width: 20 }}
+ key={icon.toString()}
+ size="sm"
+ icon={icon}
+ onPointerEnter={action(e => (this.subEndLink = (pinLayout ? 'Layout' : '') + (pinLayout && pinContent ? ' &' : '') + (pinContent ? ' Content' : '')))}
+ onPointerLeave={action(e => (this.subEndLink = ''))}
+ onClick={e => {
+ this.view0 &&
+ DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.view0.props.Document, true, this.view0, {
+ pinDocLayout: pinLayout,
+ pinData: !pinContent ? {} : { poslayoutview: true, dataannos: true, dataview: pinContent },
+ } as PinProps);
+
+ e.stopPropagation();
+ }}
+ />
+ </div>
+ </Tooltip>
+ );
+ };
+ return !this.view0 ? null : (
+ <div className="documentButtonBar-icon documentButtonBar-pin">
+ <div className="documentButtonBar-pinTypes">
+ {linkBtn(true, false, 'window-maximize')}
+ {linkBtn(false, true, 'address-card')}
+ {linkBtn(true, true, 'id-card')}
+ </div>
+ <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={false} />
+ </div>
+ );
+ }
+
@observable subPin = '';
@computed
get pinButton() {
const targetDoc = this.view0?.props.Document;
- const pinBtn = (pinDocLayout: boolean, pinDocContent: boolean, icon: IconProp) => {
+ const pinBtn = (pinLayoutView: boolean, pinContentView: boolean, icon: IconProp) => {
const tooltip = `Pin Document and Save ${this.subPin} to trail`;
return !tooltip ? null : (
<Tooltip title={<div className="dash-tooltip">{tooltip}</div>}>
@@ -242,10 +346,10 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
onPointerEnter={action(
e =>
(this.subPin =
- (pinDocLayout ? 'Layout' : '') +
- (pinDocLayout && pinDocContent ? ' &' : '') +
- (pinDocContent ? ' Content View' : '') +
- (pinDocLayout && pinDocContent ? '(shift+alt)' : pinDocLayout ? '(shift)' : pinDocContent ? '(alt)' : ''))
+ (pinLayoutView ? 'Layout' : '') +
+ (pinLayoutView && pinContentView ? ' &' : '') +
+ (pinContentView ? ' Content View' : '') +
+ (pinLayoutView && pinContentView ? '(shift+alt)' : pinLayoutView ? '(shift)' : pinContentView ? '(alt)' : ''))
)}
onPointerLeave={action(e => (this.subPin = ''))}
onClick={e => {
@@ -253,7 +357,13 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
.views()
.filter(v => v)
.map(dv => dv!.rootDoc);
- TabDocView.PinDoc(docs, { pinDocLayout, pinDocContent, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) });
+ TabDocView.PinDoc(docs, {
+ pinAudioPlay: true,
+ pinDocLayout: pinLayoutView,
+ pinData: { dataview: pinContentView },
+ activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null),
+ currentFrame: Cast(docs.lastElement()?.currentFrame, 'number', null),
+ });
e.stopPropagation();
}}
/>
@@ -264,25 +374,20 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
return !targetDoc ? null : (
<Tooltip title={<div className="dash-tooltip">{`Pin Document ${SelectionManager.Views().length > 1 ? 'multiple documents' : ''} to Trail`}</div>}>
<div
- className="documentButtonBar-icon"
- style={{ color: 'white' }}
- onPointerEnter={action(e => (this.expandPin = true))}
- onPointerLeave={action(e => (this.expandPin = false))}
+ className="documentButtonBar-icon documentButtonBar-pin"
onClick={e => {
const docs = this.props
.views()
.filter(v => v)
.map(dv => dv!.rootDoc);
- TabDocView.PinDoc(docs, { pinDocLayout: e.shiftKey, pinDocContent: e.altKey, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) });
+ TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: e.shiftKey, pinData: { dataview: e.altKey }, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) });
e.stopPropagation();
}}>
- {this.expandPin ? (
- <div className="documentButtonBar-pinTypes">
- {pinBtn(true, false, 'window-maximize')}
- {pinBtn(false, true, 'address-card')}
- {pinBtn(true, true, 'id-card')}
- </div>
- ) : null}
+ <div className="documentButtonBar-pinTypes">
+ {pinBtn(true, false, 'window-maximize')}
+ {pinBtn(false, true, 'address-card')}
+ {pinBtn(true, true, 'id-card')}
+ </div>
<FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
</div>
</Tooltip>
@@ -293,12 +398,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
get shareButton() {
const targetDoc = this.view0?.props.Document;
return !targetDoc ? null : (
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{'Open Sharing Manager'}</div>
- </>
- }>
+ <Tooltip title={<div className="dash-tooltip">{'Open Sharing Manager'}</div>}>
<div className="documentButtonBar-icon" style={{ color: 'white' }} onClick={e => SharingManager.Instance.open(this.view0, targetDoc)}>
<FontAwesomeIcon className="documentdecorations-icon" icon="users" />
</div>
@@ -311,7 +411,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
const targetDoc = this.view0?.props.Document;
return !targetDoc ? null : (
<Tooltip title={<div className="dash-tooltip">{`Open Context Menu`}</div>}>
- <div className="documentButtonBar-icon" style={{ color: 'white', cursor: 'pointer' }} onClick={e => this.openContextMenu(e)}>
+ <div className="documentButtonBar-icon" style={{ color: 'white', cursor: 'pointer' }} onClick={this.openContextMenu}>
<FontAwesomeIcon className="documentdecorations-icon" icon="bars" />
</div>
</Tooltip>
@@ -321,12 +421,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
get metadataButton() {
const view0 = this.view0;
return !view0 ? null : (
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">Show metadata panel</div>
- </>
- }>
+ <Tooltip title={<div className="dash-tooltip">Show metadata panel</div>}>
<div className="documentButtonBar-linkFlyout">
<Flyout
anchorPoint={anchorPoints.LEFT_TOP}
@@ -349,44 +444,46 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
}
@observable _isRecording = false;
+ _stopFunc: () => void = emptyFunction;
@computed
get recordButton() {
const targetDoc = this.view0?.props.Document;
return !targetDoc ? null : (
- <Tooltip title={<div className="dash-tooltip">{'Click to record 5 second annotation'}</div>}>
+ <Tooltip title={<div className="dash-tooltip">Press to record audio annotation</div>}>
<div
className="documentButtonBar-icon"
- style={{ backgroundColor: this._isRecording ? 'red' : targetDoc.isLinkButton ? Colors.LIGHT_BLUE : Colors.DARK_GRAY, color: targetDoc.isLinkButton ? Colors.BLACK : Colors.WHITE }}
- onClick={undoBatch(
- action(e => {
- this._isRecording = true;
- this.props.views().map(
- view =>
- view &&
- DocumentViewInternal.recordAudioAnnotation(
- view.dataDoc,
- view.LayoutFieldKey,
- action(() => (this._isRecording = false))
- )
- );
- })
- )}>
+ style={{ backgroundColor: this._isRecording ? Colors.ERROR_RED : Colors.DARK_GRAY, color: Colors.WHITE }}
+ onPointerDown={action((e: React.PointerEvent) => {
+ this._isRecording = true;
+ this.props.views().map(view => view && DocumentViewInternal.recordAudioAnnotation(view.dataDoc, view.LayoutFieldKey, stopFunc => (this._stopFunc = stopFunc), emptyFunction));
+ const b = UndoManager.StartBatch('Recording');
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ action(() => {
+ this._isRecording = false;
+ this._stopFunc();
+ }),
+ emptyFunction
+ );
+ })}>
<FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="microphone" />
</div>
</Tooltip>
);
}
- @observable _aliasDown = false;
+ @observable _embedDown = false;
onTemplateButton = action((e: React.PointerEvent): void => {
this._tooltipOpen = false;
- setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction);
+ setupMoveUpEvents(this, e, this.onEmbedButtonMoved, emptyFunction, emptyFunction);
});
- onAliasButtonMoved = () => {
+ onEmbedButtonMoved = () => {
if (this._dragRef.current) {
const dragDocView = this.view0!;
const dragData = new DragManager.DocumentDragData([dragDocView.props.Document]);
const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
- dragData.defaultDropAction = 'alias';
+ dragData.defaultDropAction = 'embed';
dragData.canEmbed = true;
DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, { hideSource: false });
return true;
@@ -401,14 +498,14 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
const view0 = this.view0;
const views = this.props.views();
return !view0 ? null : (
- <Tooltip title={<div className="dash-tooltip">Tap to Customize Layout. Drag an embeddable alias</div>} open={this._tooltipOpen} onClose={action(() => (this._tooltipOpen = false))} placement="bottom">
+ <Tooltip title={<div className="dash-tooltip">Tap to Customize Layout. Drag an embedding</div>} open={this._tooltipOpen} onClose={action(() => (this._tooltipOpen = false))} placement="bottom">
<div className="documentButtonBar-linkFlyout" ref={this._dragRef} onPointerEnter={action(() => !this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))}>
<Flyout
anchorPoint={anchorPoints.LEFT_TOP}
- onOpen={action(() => (this._aliasDown = true))}
- onClose={action(() => (this._aliasDown = false))}
+ onOpen={action(() => (this._embedDown = true))}
+ onClose={action(() => (this._embedDown = false))}
content={
- !this._aliasDown ? null : (
+ !this._embedDown ? null : (
<div ref={this._ref}>
{' '}
<TemplateMenu docViews={views.filter(v => v).map(v => v as DocumentView)} />
@@ -416,7 +513,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
)
}>
<div className={'documentButtonBar-linkButton-empty'} ref={this._dragRef} onPointerDown={this.onTemplateButton}>
- {<FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="sm" />}
+ <FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="sm" />
</div>
</Flyout>
</div>
@@ -436,6 +533,45 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
simulateMouseClick(child, e.clientX, e.clientY - 30, e.screenX, e.screenY - 30);
};
+ @observable _showLinkPopup = false;
+ @action
+ toggleLinkSearch = (e: React.PointerEvent) => {
+ this._showLinkPopup = !this._showLinkPopup;
+ e.stopPropagation();
+ };
+
+ @observable _captureEndLinkLayout = false;
+ @action
+ captureEndLinkLayout = (e: React.PointerEvent) => {
+ this._captureEndLinkLayout = !this._captureEndLinkLayout;
+ };
+ @observable _captureEndLinkContent = false;
+ @action
+ captureEndLinkContent = (e: React.PointerEvent) => {
+ this._captureEndLinkContent = !this._captureEndLinkContent;
+ };
+
+ @action
+ captureEndLinkState = (e: React.PointerEvent) => {
+ this._captureEndLinkContent = this._captureEndLinkLayout = !this._captureEndLinkLayout;
+ };
+
+ @action
+ toggleTrail = (e: React.PointerEvent) => {
+ const rootView = this.props.views()[0];
+ const rootDoc = rootView?.rootDoc;
+ if (rootDoc) {
+ const anchor = rootView.ComponentView?.getAnchor?.(false) ?? rootDoc;
+ const trail = DocCast(anchor.presTrail) ?? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail), true);
+ if (trail !== anchor.presTrail) {
+ DocUtils.MakeLink(anchor, trail, { link_relationship: '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;
@@ -448,23 +584,28 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
<div className="documentButtonBar-button">
<DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} ShowCount={true} />
</div>
- <div className="documentButtonBar-button">
- <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={true} />
- </div>
- {(DocumentLinksButton.StartLink || Doc.UserDoc()['documentLinksButton-fullMenu']) && DocumentLinksButton.StartLink !== doc ? (
- <div className="documentButtonBar-button">
- <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={false} />
+ {this._showLinkPopup ? (
+ <div style={{ position: 'absolute', zIndex: 1000 }}>
+ <LinkPopup
+ key="popup"
+ showPopup={this._showLinkPopup}
+ linkCreated={link => (link.link_displayLine = !IsFollowLinkScript(this.props.views().lastElement()?.rootDoc.onClick))}
+ linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.(true)}
+ linkFrom={() => this.props.views().lastElement()?.rootDoc}
+ />
</div>
- ) : null}
- {Doc.noviceMode ? null : <div className="documentButtonBar-button">{this.recordButton}</div>}
+ ) : (
+ <div className="documentButtonBar-button">{this.linkButton}</div>
+ )}
+
+ {DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== doc ? <div className="documentButtonBar-button">{this.endLinkButton} </div> : null}
{
Doc.noviceMode ? null : <div className="documentButtonBar-button">{this.templateButton}</div>
- /*<div className="documentButtonBar-button">
- {this.metadataButton}
- </div> */
+ /*<div className="documentButtonBar-button"> {this.metadataButton} </div> */
}
{!SelectionManager.Views()?.some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>}
<div className="documentButtonBar-button">{this.pinButton}</div>
+ <div className="documentButtonBar-button">{this.recordButton}</div>
{!Doc.UserDoc()['documentLinksButton-fullMenu'] ? null : <div className="documentButtonBar-button">{this.shareButton}</div>}
{!Doc.UserDoc()['documentLinksButton-fullMenu'] ? null : (
<div className="documentButtonBar-button" style={{ display: !considerPush ? 'none' : '' }}>