aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/DocumentDecorations.tsx
diff options
context:
space:
mode:
authorbob <bcz@cs.brown.edu>2019-09-19 16:38:37 -0400
committerbob <bcz@cs.brown.edu>2019-09-19 16:38:37 -0400
commit13e3611f87a941fe29ff0256890cdf3c74351bab (patch)
tree3585253140731387551ba4a2cfa0e00e8755c482 /src/client/views/DocumentDecorations.tsx
parente95a771cb0bcc3add66ebd28344d6a303e7b2bca (diff)
fixes to button bar
Diffstat (limited to 'src/client/views/DocumentDecorations.tsx')
-rw-r--r--src/client/views/DocumentDecorations.tsx296
1 files changed, 9 insertions, 287 deletions
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2b121b32b..cbf812311 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -6,31 +6,23 @@ import { observer } from "mobx-react";
import { Doc, DocListCastAsync } from "../../new_fields/Doc";
import { List } from "../../new_fields/List";
import { ObjectField } from '../../new_fields/ObjectField';
-import { RichTextField } from '../../new_fields/RichTextField';
import { BoolCast, Cast, NumCast, StrCast } from "../../new_fields/Types";
-import { URLField } from '../../new_fields/URLField';
import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
import { emptyFunction, Utils } from "../../Utils";
-import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
import { Docs, DocUtils } from "../documents/Documents";
import { DocumentManager } from "../util/DocumentManager";
-import { DragLinksAsDocuments, DragManager } from "../util/DragManager";
-import { LinkManager } from '../util/LinkManager';
+import { DragManager } from "../util/DragManager";
import { SelectionManager } from "../util/SelectionManager";
import { undoBatch, UndoManager } from "../util/UndoManager";
import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
import { CollectionView } from "./collections/CollectionView";
+import { DocumentButtonBar } from './DocumentButtonBar';
import './DocumentDecorations.scss';
-import { LinkMenu } from "./linking/LinkMenu";
-import { MetadataEntryMenu } from './MetadataEntryMenu';
import { PositionDocument } from './nodes/CollectionFreeFormDocumentView';
import { DocumentView, swapViews } from "./nodes/DocumentView";
import { FieldView } from "./nodes/FieldView";
-import { FormattedTextBox, GoogleRef } from "./nodes/FormattedTextBox";
+import { FormattedTextBox } from "./nodes/FormattedTextBox";
import { IconBox } from "./nodes/IconBox";
-import { ImageBox } from './nodes/ImageBox';
-import { TemplateMenu } from "./TemplateMenu";
-import { Template, Templates } from "./Templates";
import React = require("react");
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
@@ -47,28 +39,21 @@ library.add(faCloudUploadAlt);
library.add(faSyncAlt);
library.add(faShare);
-const cloud: IconProp = "cloud-upload-alt";
-const fetch: IconProp = "sync-alt";
-
@observer
export class DocumentDecorations extends React.Component<{}, { value: string }> {
static Instance: DocumentDecorations;
private _isPointerDown = false;
private _resizing = "";
- private keyinput: React.RefObject<HTMLInputElement>;
+ private _keyinput: React.RefObject<HTMLInputElement>;
private _resizeBorderWidth = 16;
private _linkBoxHeight = 20 + 3; // link button height + margin
private _titleHeight = 20;
- private _linkButton = React.createRef<HTMLDivElement>();
- private _linkerButton = React.createRef<HTMLDivElement>();
private _embedButton = React.createRef<HTMLDivElement>();
- private _tooltipoff = React.createRef<HTMLDivElement>();
- private _textDoc?: Doc;
private _downX = 0;
private _downY = 0;
private _iconDoc?: Doc = undefined;
private _resizeUndo?: UndoManager.Batch;
- private _linkDrag?: UndoManager.Batch;
+ private _radiusDown = [0, 0];
@observable private _minimizedX = 0;
@observable private _minimizedY = 0;
@observable private _title: string = "";
@@ -78,58 +63,17 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
@observable private _opacity = 1;
@observable private _removeIcon = false;
@observable public Interacting = false;
- @observable private _isMoving = false;
@observable public pushIcon: IconProp = "arrow-alt-circle-up";
@observable public pullIcon: IconProp = "arrow-alt-circle-down";
@observable public pullColor: string = "white";
@observable public isAnimatingFetch = false;
@observable public openHover = false;
- public pullColorAnimating = false;
-
- private pullAnimating = false;
- private pushAnimating = false;
-
- public startPullOutcome = action((success: boolean) => {
- if (!this.pullAnimating) {
- this.pullAnimating = true;
- this.pullIcon = success ? "check-circle" : "stop-circle";
- setTimeout(() => runInAction(() => {
- this.pullIcon = "arrow-alt-circle-down";
- this.pullAnimating = false;
- }), 1000);
- }
- });
-
- public startPushOutcome = action((success: boolean) => {
- if (!this.pushAnimating) {
- this.pushAnimating = true;
- this.pushIcon = success ? "check-circle" : "stop-circle";
- setTimeout(() => runInAction(() => {
- this.pushIcon = "arrow-alt-circle-up";
- this.pushAnimating = false;
- }), 1000);
- }
- });
-
- public setPullState = action((unchanged: boolean) => {
- this.isAnimatingFetch = false;
- if (!this.pullColorAnimating) {
- this.pullColorAnimating = true;
- this.pullColor = unchanged ? "lawngreen" : "red";
- setTimeout(this.clearPullColor, 1000);
- }
- });
-
- private clearPullColor = action(() => {
- this.pullColor = "white";
- this.pullColorAnimating = false;
- });
constructor(props: Readonly<{}>) {
super(props);
DocumentDecorations.Instance = this;
- this.keyinput = React.createRef();
+ this._keyinput = React.createRef();
reaction(() => SelectionManager.SelectedDocuments().slice(), docs => this._edtingTitle = false);
}
@@ -410,7 +354,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
iconDoc.y = where[1] + NumCast(selView.props.Document.y);
}
- _radiusDown = [0, 0];
@action
onRadiusDown = (e: React.PointerEvent): void => {
e.stopPropagation();
@@ -426,7 +369,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
onRadiusMove = (e: PointerEvent): void => {
- this._isMoving = true;
let dist = Math.sqrt((e.clientX - this._radiusDown[0]) * (e.clientX - this._radiusDown[0]) + (e.clientY - this._radiusDown[1]) * (e.clientY - this._radiusDown[1]));
dist = dist < 3 ? 0 : dist;
let usingRule = false;
@@ -447,7 +389,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
e.preventDefault();
this._isPointerDown = false;
this._resizeUndo && this._resizeUndo.end();
- this._isMoving = false;
document.removeEventListener("pointermove", this.onRadiusMove);
document.removeEventListener("pointerup", this.onRadiusUp);
}
@@ -467,13 +408,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
}
- onLinkerButtonDown = (e: React.PointerEvent): void => {
- e.stopPropagation();
- document.removeEventListener("pointermove", this.onLinkerButtonMoved);
- document.addEventListener("pointermove", this.onLinkerButtonMoved);
- document.removeEventListener("pointerup", this.onLinkerButtonUp);
- document.addEventListener("pointerup", this.onLinkerButtonUp);
- }
onEmbedButtonDown = (e: React.PointerEvent): void => {
e.stopPropagation();
@@ -483,11 +417,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
document.addEventListener("pointerup", this.onEmbedButtonUp);
}
- onLinkerButtonUp = (e: PointerEvent): void => {
- document.removeEventListener("pointermove", this.onLinkerButtonMoved);
- document.removeEventListener("pointerup", this.onLinkerButtonUp);
- e.stopPropagation();
- }
+
onEmbedButtonUp = (e: PointerEvent): void => {
document.removeEventListener("pointermove", this.onEmbedButtonMoved);
@@ -496,31 +426,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
@action
- onLinkerButtonMoved = (e: PointerEvent): void => {
- if (this._linkerButton.current !== null) {
- document.removeEventListener("pointermove", this.onLinkerButtonMoved);
- document.removeEventListener("pointerup", this.onLinkerButtonUp);
- let selDoc = SelectionManager.SelectedDocuments()[0];
- let container = selDoc.props.ContainingCollectionDoc ? selDoc.props.ContainingCollectionDoc.proto : undefined;
- let dragData = new DragManager.LinkDragData(selDoc.props.Document, container ? [container] : []);
- FormattedTextBox.InputBoxOverlay = undefined;
- this._linkDrag = UndoManager.StartBatch("Drag Link");
- DragManager.StartLinkDrag(this._linkerButton.current, dragData, e.pageX, e.pageY, {
- handlers: {
- dragComplete: () => {
- if (this._linkDrag) {
- this._linkDrag.end();
- this._linkDrag = undefined;
- }
- },
- },
- hideSource: false
- });
- }
- e.stopPropagation();
- }
-
- @action
onEmbedButtonMoved = (e: PointerEvent): void => {
if (this._embedButton.current !== null) {
document.removeEventListener("pointermove", this.onEmbedButtonMoved);
@@ -539,29 +444,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
e.stopPropagation();
}
- onLinkButtonDown = (e: React.PointerEvent): void => {
- e.stopPropagation();
- document.removeEventListener("pointermove", this.onLinkButtonMoved);
- document.addEventListener("pointermove", this.onLinkButtonMoved);
- document.removeEventListener("pointerup", this.onLinkButtonUp);
- document.addEventListener("pointerup", this.onLinkButtonUp);
- }
-
- onLinkButtonUp = (e: PointerEvent): void => {
- document.removeEventListener("pointermove", this.onLinkButtonMoved);
- document.removeEventListener("pointerup", this.onLinkButtonUp);
- e.stopPropagation();
- }
-
- onLinkButtonMoved = async (e: PointerEvent) => {
- if (this._linkButton.current !== null && (e.movementX > 1 || e.movementY > 1)) {
- document.removeEventListener("pointermove", this.onLinkButtonMoved);
- document.removeEventListener("pointerup", this.onLinkButtonUp);
- DragLinksAsDocuments(this._linkButton.current, e.x, e.y, SelectionManager.SelectedDocuments()[0].props.Document);
- }
- e.stopPropagation();
- }
-
onPointerMove = (e: PointerEvent): void => {
e.stopPropagation();
e.preventDefault();
@@ -690,134 +572,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
return "-unset-";
}
- changeFlyoutContent = (): void => {
- }
- // buttonOnPointerUp = (e: React.PointerEvent): void => {
- // e.stopPropagation();
- // }
-
- considerEmbed = () => {
- let thisDoc = SelectionManager.SelectedDocuments()[0].props.Document;
- let canEmbed = thisDoc.data && thisDoc.data instanceof URLField;
- if (!canEmbed) return (null);
- return (
- <div className="linkButtonWrapper">
- <div title="Drag Embed" className="linkButton-linker" ref={this._embedButton} onPointerDown={this.onEmbedButtonDown}>
- <FontAwesomeIcon className="documentdecorations-icon" icon="image" size="sm" />
- </div>
- </div>
- );
- }
-
- private get targetDoc() {
- return SelectionManager.SelectedDocuments()[0].props.Document;
- }
-
- considerGoogleDocsPush = () => {
- let canPush = this.targetDoc.data && this.targetDoc.data instanceof RichTextField;
- if (!canPush) return (null);
- let published = Doc.GetProto(this.targetDoc)[GoogleRef] !== undefined;
- let icon: IconProp = published ? (this.pushIcon as any) : cloud;
- return (
- <div className={"linkButtonWrapper"}>
- <div title={`${published ? "Push" : "Publish"} to Google Docs`} className="linkButton-linker" onClick={() => {
- DocumentDecorations.hasPushedHack = false;
- this.targetDoc[Pushes] = NumCast(this.targetDoc[Pushes]) + 1;
- }}>
- <FontAwesomeIcon className="documentdecorations-icon" icon={icon} size={published ? "sm" : "xs"} />
- </div>
- </div>
- );
- }
-
- considerGoogleDocsPull = () => {
- let canPull = this.targetDoc.data && this.targetDoc.data instanceof RichTextField;
- let dataDoc = Doc.GetProto(this.targetDoc);
- if (!canPull || !dataDoc[GoogleRef]) return (null);
- let icon = dataDoc.unchanged === false ? (this.pullIcon as any) : fetch;
- icon = this.openHover ? "share" : icon;
- let animation = this.isAnimatingFetch ? "spin 0.5s linear infinite" : "none";
- let title = `${!dataDoc.unchanged ? "Pull from" : "Fetch"} Google Docs`;
- return (
- <div className={"linkButtonWrapper"}>
- <div
- title={title}
- className="linkButton-linker"
- style={{
- backgroundColor: this.pullColor,
- transition: "0.2s ease all"
- }}
- onPointerEnter={e => e.altKey && runInAction(() => this.openHover = true)}
- onPointerLeave={() => runInAction(() => this.openHover = false)}
- onClick={e => {
- if (e.altKey) {
- e.preventDefault();
- window.open(`https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`);
- } else {
- this.clearPullColor();
- DocumentDecorations.hasPulledHack = false;
- this.targetDoc[Pulls] = NumCast(this.targetDoc[Pulls]) + 1;
- dataDoc.unchanged && runInAction(() => this.isAnimatingFetch = true);
- }
- }}>
- <FontAwesomeIcon
- style={{
- WebkitAnimation: animation,
- MozAnimation: animation
- }}
- className="documentdecorations-icon"
- icon={icon}
- size="sm"
- />
- </div>
- </div>
- );
- }
-
- public static hasPushedHack = false;
- public static hasPulledHack = false;
-
- considerTooltip = () => {
- let thisDoc = SelectionManager.SelectedDocuments()[0].props.Document;
- let isTextDoc = thisDoc.data && thisDoc.data instanceof RichTextField;
- if (!isTextDoc) return null;
- this._textDoc = thisDoc;
- return (
- <div className="tooltipwrapper">
- <div title="Hide Tooltip" className="linkButton-linker" ref={this._tooltipoff} onPointerDown={this.onTooltipOff}>
- {/* <FontAwesomeIcon className="fa-image" icon="image" size="sm" /> */}
- </div>
- </div>
-
- );
- }
-
- onTooltipOff = (e: React.PointerEvent): void => {
- e.stopPropagation();
- if (this._textDoc) {
- if (this._tooltipoff.current) {
- if (this._tooltipoff.current.title === "Hide Tooltip") {
- this._tooltipoff.current.title = "Show Tooltip";
- this._textDoc.tooltip = "hi";
- }
- else {
- this._tooltipoff.current.title = "Hide Tooltip";
- }
- }
- }
- }
-
- get metadataMenu() {
- return (
- <div className="linkButtonWrapper">
- <Flyout anchorPoint={anchorPoints.TOP_LEFT}
- content={<MetadataEntryMenu docs={() => SelectionManager.SelectedDocuments().map(dv => dv.props.Document)} suggestWithFunction />}>{/* tfs: @bcz This might need to be the data document? */}
- <div className="docDecs-tagButton" title="Add fields"><FontAwesomeIcon className="documentdecorations-icon" icon="tag" size="sm" /></div>
- </Flyout>
- </div>
- );
- }
render() {
var bounds = this.Bounds;
@@ -830,24 +585,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
{SelectionManager.SelectedDocuments().length === 1 ? IconBox.DocumentIcon(StrCast(SelectionManager.SelectedDocuments()[0].props.Document.layout, "...")) : "..."}
</div>);
- let linkButton = null;
- if (SelectionManager.SelectedDocuments().length > 0) {
- let selFirst = SelectionManager.SelectedDocuments()[0];
-
- let linkCount = LinkManager.Instance.getAllRelatedLinks(selFirst.props.Document).length;
- linkButton = (<Flyout
- anchorPoint={anchorPoints.RIGHT_TOP}
- content={<LinkMenu docView={selFirst}
- addDocTab={selFirst.props.addDocTab}
- changeFlyout={this.changeFlyoutContent} />}>
- <div className={"linkButton-" + (linkCount ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >{linkCount}</div>
- </Flyout >);
- }
-
- let templates: Map<Template, boolean> = new Map();
- Array.from(Object.values(Templates.TemplateList)).map(template =>
- templates.set(template, SelectionManager.SelectedDocuments().reduce((checked, doc) => checked || (doc.props.Document["show" + template.Name] ? true : false), false as boolean)));
-
bounds.x = Math.max(0, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
bounds.y = Math.max(0, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight;
const borderRadiusDraggerWidth = 15;
@@ -879,7 +616,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
{minimizeIcon}
{this._edtingTitle ?
- <input ref={this.keyinput} className="title" type="text" name="dynbox" value={this._title} onBlur={this.titleBlur} onChange={this.titleChanged} onKeyPress={this.titleEntered} /> :
+ <input ref={this._keyinput} className="title" type="text" name="dynbox" value={this._title} onBlur={this.titleBlur} onChange={this.titleChanged} onKeyPress={this.titleEntered} /> :
<div className="title" onPointerDown={this.onTitleDown} ><span>{`${this.selectionTitle}`}</span></div>}
<div className="documentDecorations-closeButton" title="Close Document" onPointerDown={this.onCloseDown}>
<FontAwesomeIcon className="documentdecorations-times" icon={faTimes} size="lg" />
@@ -895,22 +632,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
<div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
<div id="documentDecorations-borderRadius" className="documentDecorations-radius" onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}><span className="borderRadiusTooltip" title="Drag Corner Radius"></span></div>
<div className="link-button-container">
- <div className="linkButtonWrapper">
- <div title="View Links" className="linkFlyout" ref={this._linkButton}> {linkButton} </div>
- </div>
- <div className="linkButtonWrapper">
- <div title="Drag Link" className="linkButton-linker" ref={this._linkerButton} onPointerDown={this.onLinkerButtonDown}>
- <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" />
- </div>
- </div>
- <div className="linkButtonWrapper">
- <TemplateMenu docs={SelectionManager.ViewsSortedVertically()} templates={templates} />
- </div>
- {this.metadataMenu}
- {this.considerEmbed()}
- {this.considerGoogleDocsPush()}
- {this.considerGoogleDocsPull()}
- {/* {this.considerTooltip()} */}
+ <DocumentButtonBar views={SelectionManager.SelectedDocuments()} />
</div>
</div >
</div>