aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
authorSam Wilkins <samwilkins333@gmail.com>2019-09-22 17:16:18 -0400
committerSam Wilkins <samwilkins333@gmail.com>2019-09-22 17:16:18 -0400
commitf82458be8bc8beaab387cc2813b7b18c9b3caac2 (patch)
tree4d547ad7f2623257cc585e16df17c05c8979e06a /src/client/views/nodes
parentf529d7d22162689c08830418da67a89778111e16 (diff)
initial commit post master merge
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/ButtonBox.tsx6
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.scss5
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx95
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx9
-rw-r--r--src/client/views/nodes/DocumentView.scss42
-rw-r--r--src/client/views/nodes/DocumentView.tsx810
-rw-r--r--src/client/views/nodes/DragBox.tsx10
-rw-r--r--src/client/views/nodes/FieldView.tsx4
-rw-r--r--src/client/views/nodes/FormattedTextBox.scss1
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx165
-rw-r--r--src/client/views/nodes/IconBox.tsx52
-rw-r--r--src/client/views/nodes/ImageBox.tsx55
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx6
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx9
-rw-r--r--src/client/views/nodes/PDFBox.tsx106
-rw-r--r--src/client/views/nodes/VideoBox.tsx31
16 files changed, 644 insertions, 762 deletions
diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx
index 68d3b8ae1..f08ea4891 100644
--- a/src/client/views/nodes/ButtonBox.tsx
+++ b/src/client/views/nodes/ButtonBox.tsx
@@ -3,7 +3,7 @@ import { faEdit } from '@fortawesome/free-regular-svg-icons';
import { action, computed } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, DocListCastAsync } from '../../../new_fields/Doc';
+import { Doc, DocListCastAsync, DocListCast } from '../../../new_fields/Doc';
import { List } from '../../../new_fields/List';
import { createSchema, makeInterface, listSpec } from '../../../new_fields/Schema';
import { ScriptField } from '../../../new_fields/ScriptField';
@@ -49,7 +49,7 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt
funcs.push({
description: "Clear Script Params", event: () => {
let params = Cast(this.props.Document.buttonParams, listSpec("string"));
- params && params.map(p => this.props.Document[p] = undefined)
+ params && params.map(p => this.props.Document[p] = undefined);
}, icon: "trash"
});
@@ -68,7 +68,7 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt
render() {
let params = Cast(this.props.Document.buttonParams, listSpec("string"));
let missingParams = params && params.filter(p => this.props.Document[p] === undefined);
- params && params.map(async p => await DocListCastAsync(this.props.Document[p])); // bcz: really hacky form of prefetching ...
+ params && params.map(p => DocListCast(this.props.Document[p])); // bcz: really hacky form of prefetching ...
return (
<div className="buttonBox-outerDiv" ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
<div className="buttonBox-mainButton" style={{ background: StrCast(this.props.Document.backgroundColor), color: StrCast(this.props.Document.color, "black") }} >
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.scss b/src/client/views/nodes/CollectionFreeFormDocumentView.scss
new file mode 100644
index 000000000..c0d9e1267
--- /dev/null
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.scss
@@ -0,0 +1,5 @@
+.collectionFreeFormDocumentView-container {
+ transform-origin: left top;
+ position: absolute;
+ background-color: transparent;
+} \ No newline at end of file
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 07dd1cae7..9685f9bca 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,13 +1,14 @@
-import { computed } from "mobx";
+import { computed, action, observable, reaction, IReactionDisposer, trace } from "mobx";
import { observer } from "mobx-react";
-import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { BoolCast, FieldValue, NumCast, StrCast, Cast } from "../../../new_fields/Types";
+import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
+import { FieldValue, NumCast, StrCast, Cast } from "../../../new_fields/Types";
import { Transform } from "../../util/Transform";
import { DocComponent } from "../DocComponent";
-import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView";
-import "./DocumentView.scss";
+import { percent2frac } from "../../../Utils";
+import { DocumentView, DocumentViewProps, documentSchema } from "./DocumentView";
+import "./CollectionFreeFormDocumentView.scss";
import React = require("react");
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, WidthSym, HeightSym } from "../../../new_fields/Doc";
import { random } from "animejs";
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
@@ -16,31 +17,34 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
width?: number;
height?: number;
jitterRotation: number;
+ transition?: string;
}
-
-const schema = createSchema({
+export const positionSchema = createSchema({
zIndex: "number",
+ x: "number",
+ y: "number",
+ z: "number",
});
-//TODO Types: The import order is wrong, so positionSchema is undefined
-type FreeformDocument = makeInterface<[typeof schema, typeof positionSchema]>;
-const FreeformDocument = makeInterface(schema, positionSchema);
+export type PositionDocument = makeInterface<[typeof documentSchema, typeof positionSchema]>;
+export const PositionDocument = makeInterface(documentSchema, positionSchema);
@observer
-export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, FreeformDocument>(FreeformDocument) {
+export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, PositionDocument>(PositionDocument) {
+ _disposer: IReactionDisposer | undefined = undefined;
@computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${random(-1, 1) * this.props.jitterRotation}deg)`; }
- @computed get X() { return this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.Document.x || 0; }
- @computed get Y() { return this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.Document.y || 0; }
- @computed get width(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.Document.width || 0; }
- @computed get height(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.Document.height || 0; }
- @computed get nativeWidth(): number { return FieldValue(this.Document.nativeWidth, 0); }
- @computed get nativeHeight(): number { return FieldValue(this.Document.nativeHeight, 0); }
- @computed get scaleToOverridingWidth() { return this.width / NumCast(this.props.Document.width, this.width); }
+ @computed get X() { return this._animPos !== undefined ? this._animPos[0] : this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.Document.x || 0; }
+ @computed get Y() { return this._animPos !== undefined ? this._animPos[1] : this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.Document.y || 0; }
+ @computed get width() { return this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.props.Document[WidthSym](); }
+ @computed get height() { return this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.Document[HeightSym](); }
+ @computed get nativeWidth() { return FieldValue(this.Document.nativeWidth, 0); }
+ @computed get nativeHeight() { return FieldValue(this.Document.nativeHeight, 0); }
+ @computed get scaleToOverridingWidth() { return this.width / FieldValue(this.Document.width, this.width); }
@computed get renderScriptDim() {
if (this.Document.renderScript) {
- let someView = Cast(this.Document.someView, Doc);
- let minimap = Cast(this.Document.minimap, Doc);
+ let someView = Cast(this.props.Document.someView, Doc);
+ let minimap = Cast(this.props.Document.minimap, Doc);
if (someView instanceof Doc && minimap instanceof Doc) {
let x = (NumCast(someView.panX) - NumCast(someView.width) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitX) - NumCast(minimap.fitW) / 2)) / NumCast(minimap.fitW) * NumCast(minimap.width) - NumCast(minimap.width) / 2;
let y = (NumCast(someView.panY) - NumCast(someView.height) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitY) - NumCast(minimap.fitH) / 2)) / NumCast(minimap.fitH) * NumCast(minimap.height) - NumCast(minimap.height) / 2;
@@ -52,34 +56,31 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
return undefined;
}
- contentScaling = () => this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? this.width / this.nativeWidth : 1;
+ componentWillUnmount() {
+ this._disposer && this._disposer();
+ }
+ componentDidMount() {
+ this._disposer = reaction(() => [this.props.Document.animateToPos, this.props.Document.isAnimating],
+ () => {
+ const target = this.props.Document.animateToPos ? Array.from(Cast(this.props.Document.animateToPos, listSpec("number"))!) : undefined;
+ this._animPos = !target ? undefined : target[2] ? [this.Document.x || 0, this.Document.y || 0] : this.props.ScreenToLocalTransform().transformPoint(target[0], target[1]);
+ }, { fireImmediately: true });
+ }
+
+ contentScaling = () => this.nativeWidth > 0 && !this.props.Document.ignoreAspect ? this.width / this.nativeWidth : 1;
panelWidth = () => this.props.PanelWidth();
panelHeight = () => this.props.PanelHeight();
getTransform = (): Transform => this.props.ScreenToLocalTransform()
.translate(-this.X, -this.Y)
.scale(1 / this.contentScaling()).scale(1 / this.scaleToOverridingWidth)
- animateBetweenIcon = (icon: number[], stime: number, maximizing: boolean) => {
- this.props.bringToFront(this.props.Document);
- let targetPos = [this.Document.x || 0, this.Document.y || 0];
- let iconPos = this.props.ScreenToLocalTransform().transformPoint(icon[0], icon[1]);
- DocumentView.animateBetweenIconFunc(this.props.Document,
- this.Document.width || 0, this.Document.height || 0, stime, maximizing, (progress: number) => {
- let pval = maximizing ?
- [iconPos[0] + (targetPos[0] - iconPos[0]) * progress, iconPos[1] + (targetPos[1] - iconPos[1]) * progress] :
- [targetPos[0] + (iconPos[0] - targetPos[0]) * progress, targetPos[1] + (iconPos[1] - targetPos[1]) * progress];
- this.Document.x = progress === 1 ? targetPos[0] : pval[0];
- this.Document.y = progress === 1 ? targetPos[1] : pval[1];
- });
- }
-
borderRounding = () => {
- let br = StrCast(this.layoutDoc.layout instanceof Doc ? this.layoutDoc.layout.borderRounding : this.props.Document.borderRounding);
+ let ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
+ let br = StrCast(((this.layoutDoc.layout as Doc) || this.Document).borderRounding);
+ br = !br && ruleRounding ? ruleRounding : br;
if (br.endsWith("%")) {
- let percent = Number(br.substr(0, br.length - 1)) / 100;
let nativeDim = Math.min(NumCast(this.layoutDoc.nativeWidth), NumCast(this.layoutDoc.nativeHeight));
- let minDim = percent * (nativeDim ? nativeDim : Math.min(this.props.PanelWidth(), this.props.PanelHeight()));
- return minDim;
+ return percent2frac(br) * (nativeDim ? nativeDim : Math.min(this.props.PanelWidth(), this.props.PanelHeight()));
}
return undefined;
}
@@ -95,24 +96,21 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
}
+ @observable _animPos: number[] | undefined = undefined;
+
render() {
- const hasPosition = this.props.x !== undefined || this.props.y !== undefined;
return (
<div className="collectionFreeFormDocumentView-container"
style={{
- transformOrigin: "left top",
- position: "absolute",
- backgroundColor: "transparent",
boxShadow:
this.layoutDoc.opacity === 0 ? undefined : // if it's not visible, then no shadow
this.layoutDoc.z ? `#9c9396 ${StrCast(this.layoutDoc.boxShadow, "10px 10px 0.9vw")}` : // if it's a floating doc, give it a big shadow
- this.layoutDoc.isBackground ? `0px 0px 50px 50px ${this.clusterColor}` : // if it's a background & has a cluster color, make the shadow spread really big
- this.clusterColor ? (
- `${this.clusterColor} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
+ this.clusterColor ? (`${this.clusterColor} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(this.layoutDoc.isBackground ? 100 : 50) / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
+ this.layoutDoc.isBackground ? `1px 1px 1px ${this.clusterColor}` : // if it's a background & has a cluster color, make the shadow spread really big
StrCast(this.layoutDoc.boxShadow, ""),
borderRadius: this.borderRounding(),
transform: this.transform,
- transition: hasPosition ? "transform 1s" : StrCast(this.layoutDoc.transition),
+ transition: this.Document.isAnimating !== undefined ? ".5s ease-in" : this.props.transition ? this.props.transition : StrCast(this.layoutDoc.transition),
width: this.width,
height: this.height,
zIndex: this.Document.zIndex || 0,
@@ -123,7 +121,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
backgroundColor={this.clusterColorFunc}
PanelWidth={this.panelWidth}
PanelHeight={this.panelHeight}
- animateBetweenIcon={this.animateBetweenIcon}
/>
</div>
);
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index d0e117fe4..3c3cc0d91 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -93,13 +93,6 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
return { props: list };
}
- @computed get templates(): List<string> {
- let field = this.props.Document.templates;
- if (field && field instanceof List) {
- return field;
- }
- return new List<string>();
- }
@computed get finalLayout() {
return this.props.layoutKey === "overlayLayout" ? "<div/>" : this.layout;
}
@@ -107,7 +100,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
render() {
let self = this;
if (this.props.renderDepth > 7) return (null);
- if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
+ if (!this.layout && this.props.layoutKey !== "overlayLayout") return (null);
return <ObserverJsxParser
blacklistedAttrs={[]}
components={{ FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, DragBox, ButtonBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox, LinkFollowBox }}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index a91683c94..4ea200e6d 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -4,6 +4,8 @@
position: inherit;
top: 0;
left:0;
+ border-radius: inherit;
+
// background: $light-color; //overflow: hidden;
transform-origin: left top;
@@ -45,4 +47,44 @@
}
.documentView-node-topmost {
background: white;
+}
+
+.documentView-styleWrapper {
+ position: absolute;
+ display: inline-block;
+ width:100%;
+ height:100%;
+ pointer-events: none;
+
+ .documentView-styleContentWrapper {
+ width:100%;
+ display: inline-block;
+ position: absolute;
+ }
+ .documentView-titleWrapper {
+ overflow:hidden;
+ color: white;
+ transform-origin: top left;
+ top: 0;
+ height: 25;
+ background: rgba(0, 0, 0, .4);
+ padding: 4px;
+ text-align: center;
+ text-overflow: ellipsis;
+ white-space: pre;
+ }
+
+ .documentView-searchHighlight {
+ position: absolute;
+ background: yellow;
+ bottom: -20px;
+ border-radius: 5px;
+ transform-origin: bottom left;
+ }
+
+ .documentView-captionWrapper {
+ position: absolute;
+ bottom: 0;
+ transform-origin: bottom left;
+ }
} \ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9b97705a0..e98fc73e3 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,15 +1,15 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import * as fa from '@fortawesome/free-solid-svg-icons';
-import { action, computed, IReactionDisposer, reaction, runInAction, trace, observable } from "mobx";
+import { action, computed, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as rp from "request-promise";
-import { Doc, DocListCast, DocListCastAsync, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
+import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc";
import { Copy, Id } from '../../../new_fields/FieldSymbols';
import { List } from "../../../new_fields/List";
import { ObjectField } from "../../../new_fields/ObjectField";
import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema";
import { ScriptField } from '../../../new_fields/ScriptField';
-import { BoolCast, Cast, FieldValue, NumCast, StrCast, PromiseValue } from "../../../new_fields/Types";
+import { BoolCast, Cast, FieldValue, NumCast, PromiseValue, StrCast } from "../../../new_fields/Types";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
import { RouteStore } from '../../../server/RouteStore';
import { emptyFunction, returnTrue, Utils } from "../../../Utils";
@@ -35,7 +35,6 @@ import { MainView } from '../MainView';
import { OverlayView } from '../OverlayView';
import { ScriptBox } from '../ScriptBox';
import { ScriptingRepl } from '../ScriptingRepl';
-import { Template } from "./../Templates";
import { DocumentContentsView } from "./DocumentContentsView";
import "./DocumentView.scss";
import { FormattedTextBox } from './FormattedTextBox';
@@ -45,6 +44,7 @@ import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { ImageField } from '../../../new_fields/URLField';
import { IDisposable } from '../../northstar/utils/IDisposable';
import SharingManager from '../../util/SharingManager';
+import { Scripting } from '../../util/Scripting';
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
library.add(fa.faTrash);
@@ -69,19 +69,9 @@ library.add(fa.faUnlock);
library.add(fa.faLock);
library.add(fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone);
-// const linkSchema = createSchema({
-// title: "string",
-// linkDescription: "string",
-// linkTags: "string",
-// linkedTo: Doc,
-// linkedFrom: Doc
-// });
-
-// type LinkDoc = makeInterface<[typeof linkSchema]>;
-// const LinkDoc = makeInterface(linkSchema);
-
export interface DocumentViewProps {
ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
+ ContainingCollectionDoc: Opt<Doc>;
Document: Doc;
DataDoc?: Doc;
fitToBox?: boolean;
@@ -93,47 +83,51 @@ export interface DocumentViewProps {
renderDepth: number;
showOverlays?: (doc: Doc) => { title?: string, caption?: string };
ContentScaling: () => number;
+ ruleProvider: Doc | undefined;
PanelWidth: () => number;
PanelHeight: () => number;
- focus: (doc: Doc, willZoom: boolean, scale?: number) => void;
+ focus: (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: () => boolean) => void;
parentActive: () => boolean;
whenActiveChanged: (isActive: boolean) => void;
bringToFront: (doc: Doc, sendToBack?: boolean) => void;
- addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void;
+ addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => boolean;
pinToPres: (document: Doc) => void;
- collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void;
zoomToScale: (scale: number) => void;
backgroundColor: (doc: Doc) => string | undefined;
getScale: () => number;
- animateBetweenIcon?: (iconPos: number[], startTime: number, maximizing: boolean) => void;
+ animateBetweenIcon?: (maximize: boolean, target: number[]) => void;
ChromeHeight?: () => number;
}
-const schema = createSchema({
- layout: "string",
- nativeWidth: "number",
- nativeHeight: "number",
- backgroundColor: "string",
- opacity: "number",
- hidden: "boolean",
- onClick: ScriptField,
-});
-
-export const positionSchema = createSchema({
- nativeWidth: "number",
- nativeHeight: "number",
- width: "number",
- height: "number",
- x: "number",
- y: "number",
- z: "number",
+export const documentSchema = createSchema({
+ // layout: "string", // this should be a "string" or Doc, but can't do that in schemas, so best to leave it out
+ title: "string", // document title (can be on either data document or layout)
+ nativeWidth: "number", // native width of document which determines how much document contents are scaled when the document's width is set
+ nativeHeight: "number", // "
+ width: "number", // width of document in its container's coordinate system
+ height: "number", // "
+ backgroundColor: "string", // background color of document
+ opacity: "number", // opacity of document
+ onClick: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop)
+ ignoreAspect: "boolean", // whether aspect ratio should be ignored when laying out or manipulating the document
+ autoHeight: "boolean", // whether the height of the document should be computed automatically based on its contents
+ isTemplate: "boolean", // whether this document acts as a template layout for describing how other documents should be displayed
+ isBackground: "boolean", // whether document is a background element and ignores input events (can only selet with marquee)
+ type: "string", // enumerated type of document
+ maximizeLocation: "string", // flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab)
+ lockedPosition: "boolean", // whether the document can be spatially manipulated
+ borderRounding: "string", // border radius rounding of document
+ searchFields: "string", // the search fields to display when this document matches a search in its metadata
+ heading: "number", // the logical layout 'heading' of this document (used by rule provider to stylize h1 header elements, from h2, etc)
+ showCaption: "string", // whether editable caption text is overlayed at the bottom of the document
+ showTitle: "string", // whether an editable title banner is displayed at tht top of the document
+ isButton: "boolean", // whether document functions as a button (overiding native interactions of its content)
+ ignoreClick: "boolean", // whether documents ignores input clicks (but does not ignore manipulation and other events)
});
-export type PositionDocument = makeInterface<[typeof positionSchema]>;
-export const PositionDocument = makeInterface(positionSchema);
-type Document = makeInterface<[typeof schema]>;
-const Document = makeInterface(schema);
+type Document = makeInterface<[typeof documentSchema]>;
+const Document = makeInterface(documentSchema);
@observer
export class DocumentView extends DocComponent<DocumentViewProps, Document>(Document) {
@@ -141,107 +135,41 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
private _downY: number = 0;
private _lastTap: number = 0;
private _doubleTap = false;
- private _hitExpander = false;
private _hitTemplateDrag = false;
private _mainCont = React.createRef<HTMLDivElement>();
private _dropDisposer?: DragManager.DragDropDisposer;
- _animateToIconDisposer?: IReactionDisposer;
- _reactionDisposer?: IReactionDisposer;
public get ContentDiv() { return this._mainCont.current; }
- @computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
- @computed get topMost(): boolean { return this.props.renderDepth === 0; }
- screenRect = (): ClientRect | DOMRect => this._mainCont.current ? this._mainCont.current.getBoundingClientRect() : new DOMRect();
+ @computed get active() { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
+ @computed get topMost() { return this.props.renderDepth === 0; }
+ @computed get nativeWidth() { return this.Document.nativeWidth || 0; }
+ @computed get nativeHeight() { return this.Document.nativeHeight || 0; }
+ @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : this.Document.onClick; }
@action
componentDidMount() {
- if (this._mainCont.current) {
- this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, {
- handlers: { drop: this.drop.bind(this) }
- });
- }
- // bcz: kind of ugly .. setup a reaction to update the title of a summary document's target (maximizedDocs) whenver the summary doc's title changes
- this._reactionDisposer = reaction(() => [DocListCast(this.props.Document.maximizedDocs).map(md => md.title),
- this.props.Document.summaryDoc, this.props.Document.summaryDoc instanceof Doc ? this.props.Document.summaryDoc.title : ""],
- () => {
- let maxDoc = DocListCast(this.props.Document.maximizedDocs);
- if (maxDoc.length === 1 && StrCast(this.props.Document.title).startsWith("-") && StrCast(this.props.Document.layout).indexOf("IconBox") !== -1) {
- this.props.Document.proto!.title = "-" + maxDoc[0].title + ".icon";
- }
- let sumDoc = Cast(this.props.Document.summaryDoc, Doc);
- if (sumDoc instanceof Doc && StrCast(this.props.Document.title).startsWith("-")) {
- this.props.Document.proto!.title = "-" + sumDoc.title + ".expanded";
- }
- }, { fireImmediately: true });
- this._animateToIconDisposer = reaction(() => this.props.Document.isIconAnimating, (values) =>
- (values instanceof List) && this.animateBetweenIcon(values, values[2], values[3] ? true : false)
- , { fireImmediately: true });
+ this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }));
DocumentManager.Instance.DocumentViews.push(this);
}
- animateBetweenIcon = (iconPos: number[], startTime: number, maximizing: boolean) => {
- this.props.animateBetweenIcon ? this.props.animateBetweenIcon(iconPos, startTime, maximizing) :
- DocumentView.animateBetweenIconFunc(this.props.Document, this.Document[WidthSym](), this.Document[HeightSym](), startTime, maximizing);
- }
-
- public static animateBetweenIconFunc = (doc: Doc, width: number, height: number, stime: number, maximizing: boolean, cb?: (progress: number) => void) => {
- setTimeout(() => {
- let now = Date.now();
- let progress = now < stime + 200 ? Math.min(1, (now - stime) / 200) : 1;
- doc.width = progress === 1 ? width : maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress;
- doc.height = progress === 1 ? height : maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress;
- cb && cb(progress);
- if (now < stime + 200) {
- DocumentView.animateBetweenIconFunc(doc, width, height, stime, maximizing, cb);
- }
- else {
- doc.isMinimized = !maximizing;
- doc.isIconAnimating = undefined;
- }
- doc.willMaximize = false;
- },
- 2);
- }
@action
componentDidUpdate() {
this._dropDisposer && this._dropDisposer();
- if (this._mainCont.current) {
- this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, {
- handlers: { drop: this.drop.bind(this) }
- });
- }
+ this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }));
}
+
@action
componentWillUnmount() {
- this._reactionDisposer && this._reactionDisposer();
- this._animateToIconDisposer && this._animateToIconDisposer();
this._dropDisposer && this._dropDisposer();
DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
}
- stopPropagation = (e: React.SyntheticEvent) => {
- e.stopPropagation();
- }
-
- get dataDoc() {
- if (this.props.DataDoc === undefined && (this.props.Document.layout instanceof Doc || this.props.Document instanceof Promise)) {
- // if there is no dataDoc (ie, we're not rendering a temlplate layout), but this document
- // has a template layout document, then we will render the template layout but use
- // this document as the data document for the layout.
- return this.props.Document;
- }
- return this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined;
- }
- startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean, applyAsTemplate?: boolean) {
+ startDragging(x: number, y: number, dropAction: dropActionType, applyAsTemplate?: boolean) {
if (this._mainCont.current) {
- let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])];
- let alldataConnected = [this.dataDoc, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])];
+ let dragData = new DragManager.DocumentDragData([this.props.Document]);
const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0);
- let dragData = new DragManager.DocumentDragData(allConnected, alldataConnected);
- const [xoff, yoff] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
+ dragData.offset = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
dragData.dropAction = dropAction;
- dragData.xOffset = xoff;
- dragData.yOffset = yoff;
dragData.moveDocument = this.props.moveDocument;
dragData.applyAsTemplate = applyAsTemplate;
DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, {
@@ -252,151 +180,79 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
});
}
}
- toggleMinimized = async () => {
- let minimizedDoc = await Cast(this.props.Document.minimizedDoc, Doc);
- if (minimizedDoc) {
- let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(
- NumCast(minimizedDoc.x) - NumCast(this.Document.x), NumCast(minimizedDoc.y) - NumCast(this.Document.y));
- this.collapseTargetsToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs));
- }
- }
-
- static _undoBatch?: UndoManager.Batch = undefined;
- @action
- public collapseTargetsToPoint = (scrpt: number[], expandedDocs: Doc[] | undefined): void => {
- SelectionManager.DeselectAll();
- if (expandedDocs) {
- if (!DocumentView._undoBatch) {
- DocumentView._undoBatch = UndoManager.StartBatch("iconAnimating");
- }
- let isMinimized: boolean | undefined;
- expandedDocs.map(maximizedDoc => {
- let iconAnimating = Cast(maximizedDoc.isIconAnimating, List);
- if (!iconAnimating || (Date.now() - iconAnimating[2] > 1000)) {
- if (isMinimized === undefined) {
- isMinimized = BoolCast(maximizedDoc.isMinimized);
- }
- maximizedDoc.willMaximize = isMinimized;
- maximizedDoc.isMinimized = false;
- maximizedDoc.isIconAnimating = new List<number>([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]);
- }
- });
- setTimeout(() => {
- DocumentView._undoBatch && DocumentView._undoBatch.end();
- DocumentView._undoBatch = undefined;
- }, 500);
- }
- }
onClick = async (e: React.MouseEvent) => {
- if (e.nativeEvent.cancelBubble) return; // needed because EditableView may stopPropagation which won't apparently stop this event from firing.
- if (this.onClickHandler && this.onClickHandler.script) {
+ if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && CurrentUserUtils.MainDocId !== this.props.Document[Id] &&
+ (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
e.stopPropagation();
- this.onClickHandler.script.run({ this: this.props.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document });
e.preventDefault();
- return;
+ if (this._doubleTap && this.props.renderDepth) {
+ let fullScreenAlias = Doc.MakeAlias(this.props.Document);
+ let layoutNative = await PromiseValue(Cast(this.props.Document.layoutNative, Doc));
+ if (layoutNative && fullScreenAlias.layout === layoutNative.layout) {
+ await swapViews(fullScreenAlias, "layoutCustom", "layoutNative");
+ }
+ this.props.addDocTab(fullScreenAlias, undefined, "inTab");
+ SelectionManager.DeselectAll();
+ Doc.UnBrushDoc(this.props.Document);
+ } else if (this.onClickHandler && this.onClickHandler.script) {
+ this.onClickHandler.script.run({ this: this.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document }, console.log);
+ } else if (this.Document.isButton) {
+ SelectionManager.SelectDoc(this, e.ctrlKey); // don't think this should happen if a button action is actually triggered.
+ this.buttonClick(e.altKey, e.ctrlKey);
+ } else SelectionManager.SelectDoc(this, e.ctrlKey);
}
- let altKey = e.altKey;
- let ctrlKey = e.ctrlKey;
- if (this._doubleTap && this.props.renderDepth) {
- e.stopPropagation();
- let fullScreenAlias = Doc.MakeAlias(this.props.Document);
- fullScreenAlias.templates = new List<string>();
- Doc.UseDetailLayout(fullScreenAlias);
- fullScreenAlias.showCaption = true;
- this.props.addDocTab(fullScreenAlias, this.dataDoc, "inTab");
+ }
+
+ buttonClick = async (altKey: boolean, ctrlKey: boolean) => {
+ let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs);
+ let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs);
+ let linkedDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document);
+ let expandedDocs: Doc[] = [];
+ expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs;
+ expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs;
+ // let expandedDocs = [ ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),];
+ if (expandedDocs.length) {
SelectionManager.DeselectAll();
- Doc.UnBrushDoc(this.props.Document);
- }
- else if (CurrentUserUtils.MainDocId !== this.props.Document[Id] &&
- (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
- Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
- if (BoolCast(this.props.Document.ignoreClick)) {
- return;
+ let maxLocation = StrCast(this.Document.maximizeLocation, "inPlace");
+ maxLocation = this.Document.maximizeLocation = (!ctrlKey ? !altKey ? maxLocation : (maxLocation !== "inPlace" ? "inPlace" : "onRight") : (maxLocation !== "inPlace" ? "inPlace" : "inTab"));
+ if (maxLocation === "inPlace") {
+ expandedDocs.forEach(maxDoc => this.props.addDocument && this.props.addDocument(maxDoc, false));
+ let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2);
+ DocumentManager.Instance.animateBetweenPoint(scrpt, expandedDocs);
+ } else {
+ expandedDocs.forEach(maxDoc => (!this.props.addDocTab(maxDoc, undefined, "close") && this.props.addDocTab(maxDoc, undefined, maxLocation)));
}
- e.stopPropagation();
- SelectionManager.SelectDoc(this, e.ctrlKey);
- let isExpander = (e.target as any).id === "isExpander";
- if (BoolCast(this.props.Document.isButton) || this.props.Document.type === DocumentType.BUTTON || isExpander) {
- let subBulletDocs = await DocListCastAsync(this.props.Document.subBulletDocs);
- let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs);
- let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs);
- let linkedDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document);
- let expandedDocs: Doc[] = [];
- expandedDocs = subBulletDocs ? [...subBulletDocs, ...expandedDocs] : expandedDocs;
- expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs;
- expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs;
- // let expandedDocs = [...(subBulletDocs ? subBulletDocs : []), ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),];
- if (expandedDocs.length) { // bcz: need a better way to associate behaviors with click events on widget-documents
- SelectionManager.DeselectAll();
- let maxLocation = StrCast(this.props.Document.maximizeLocation, "inPlace");
- let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target);
- if (altKey || ctrlKey) {
- maxLocation = this.props.Document.maximizeLocation = (ctrlKey ? maxLocation : (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace"));
- if (!maxLocation || maxLocation === "inPlace") {
- let hadView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0], this.props.ContainingCollectionView);
- let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !BoolCast(d.IsMinimized), false);
- expandedDocs.forEach(maxDoc => Doc.GetProto(maxDoc).isMinimized = false);
- let hasView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0], this.props.ContainingCollectionView);
- if (!hasView) {
- this.props.addDocument && expandedDocs.forEach(async maxDoc => this.props.addDocument!(getDispDoc(maxDoc), false));
- }
- expandedDocs.forEach(maxDoc => maxDoc.isMinimized = wasMinimized);
- }
- }
- if (maxLocation && maxLocation !== "inPlace" && CollectionDockingView.Instance) {
- let dataDocs = DocListCast(CollectionDockingView.Instance.props.Document.data);
- if (dataDocs) {
- expandedDocs.forEach(maxDoc =>
- (!CollectionDockingView.Instance.CloseRightSplit(Doc.GetProto(maxDoc)) &&
- this.props.addDocTab(getDispDoc(maxDoc), undefined, maxLocation)));
- }
- } else {
- let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2);
- this.collapseTargetsToPoint(scrpt, expandedDocs);
- }
- }
- else if (linkedDocs.length) {
- SelectionManager.DeselectAll();
- let first = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor1 as Doc, this.props.Document));
- let linkedFwdDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : [expandedDocs[0], expandedDocs[0]];
-
- // @TODO: shouldn't always follow target context
- let linkedFwdContextDocs = [first.length ? await (first[0].targetContext) as Doc : undefined, undefined];
-
- let linkedFwdPage = [first.length ? NumCast(first[0].anchor2Page, undefined) : undefined, undefined];
-
- if (!linkedFwdDocs.some(l => l instanceof Promise)) {
- let maxLocation = StrCast(linkedFwdDocs[0].maximizeLocation, "inTab");
- let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined;
- DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false,
- document => { // open up target if it's not already in view ...
- let cv = this.props.ContainingCollectionView; // bcz: ugh --- maybe need to have a props.unfocus() method so that we leave things in the state we found them??
- let px = cv && cv.props.Document.panX;
- let py = cv && cv.props.Document.panY;
- let s = cv && cv.props.Document.scale;
- this.props.focus(this.props.Document, true, 1); // by zooming into the button document first
- setTimeout(() => {
- this.props.addDocTab(document, undefined, maxLocation);
- cv && (cv.props.Document.panX = px);
- cv && (cv.props.Document.panY = py);
- cv && (cv.props.Document.scale = s);
- }, 1000); // then after the 1sec animation, open up the target in a new tab
- },
- linkedFwdPage[altKey ? 1 : 0], targetContext);
- }
- }
+ }
+ else if (linkedDocs.length) {
+ SelectionManager.DeselectAll();
+ let first = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor1 as Doc, this.props.Document) && !d.anchor1anchored);
+ let firstUnshown = first.filter(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
+ if (firstUnshown.length) first = [firstUnshown[0]];
+ let linkedFwdDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : [expandedDocs[0], expandedDocs[0]];
+
+ // @TODO: shouldn't always follow target context
+ let linkedFwdContextDocs = [first.length ? await (first[0].targetContext) as Doc : undefined, undefined];
+ let linkedFwdPage = [first.length ? NumCast(first[0].anchor2Page, undefined) : undefined, undefined];
+
+ if (!linkedFwdDocs.some(l => l instanceof Promise)) {
+ let maxLocation = StrCast(linkedFwdDocs[0].maximizeLocation, "inTab");
+ let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionDoc) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined;
+ DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false,
+ // open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards
+ doc => this.props.focus(this.props.Document, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation)),
+ linkedFwdPage[altKey ? 1 : 0], targetContext);
}
}
}
-
onPointerDown = (e: React.PointerEvent): void => {
if (e.nativeEvent.cancelBubble) return;
this._downX = e.clientX;
this._downY = e.clientY;
- this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0;
this._hitTemplateDrag = false;
+ // this whole section needs to move somewhere else. We're trying to initiate a special "template" drag where
+ // this document is the template and we apply it to whatever we drop it on.
for (let element = (e.target as any); element && !this._hitTemplateDrag; element = element.parentElement) {
if (element.className && element.className.toString() === "collectionViewBaseChrome-collapse") {
this._hitTemplateDrag = true;
@@ -413,11 +269,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
document.removeEventListener("pointermove", this.onPointerMove);
}
else if (!e.cancelBubble && this.active) {
- if (!this.props.Document.excludeFromLibrary && (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3)) {
- if (!e.altKey && !this.topMost && e.buttons === 1 && !BoolCast(this.props.Document.lockedPosition)) {
+ if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
+ if (!e.altKey && !this.topMost && e.buttons === 1 && !BoolCast(this.Document.lockedPosition)) {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
- this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitExpander, this._hitTemplateDrag);
+ this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag);
}
}
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
@@ -435,77 +291,72 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
deleteClicked = (): void => { SelectionManager.DeselectAll(); this.props.removeDocument && this.props.removeDocument(this.props.Document); }
@undoBatch
- fieldsClicked = (): void => {
- let kvp = Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 });
- this.props.addDocTab(kvp, this.dataDoc, "onRight");
- }
+ static makeNativeViewClicked = (doc: Doc): void => { swapViews(doc, "layoutNative", "layoutCustom"); }
@undoBatch
- makeBtnClicked = (): void => {
- let doc = Doc.GetProto(this.props.Document);
- if (doc.isButton || doc.onClick) {
- doc.isButton = false;
- doc.onClick = undefined;
+ static makeCustomViewClicked = async (doc: Doc, dataDoc: Opt<Doc>) => {
+ if (doc.layoutCustom === undefined) {
+ Doc.GetProto(dataDoc || doc).layoutNative = Doc.MakeTitled("layoutNative");
+ await swapViews(doc, "", "layoutNative");
+
+ const width = NumCast(doc.width);
+ const height = NumCast(doc.height);
+ const options = { title: "data", width, x: -width / 2, y: - height / 2, };
+ let fieldTemplate = doc.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) :
+ doc.type === DocumentType.VID ? Docs.Create.VideoDocument("http://www.cs.brown.edu", options) :
+ Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
+
+ fieldTemplate.backgroundColor = doc.backgroundColor;
+ fieldTemplate.heading = 1;
+ fieldTemplate.autoHeight = true;
+
+ let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: doc.title + "_layout", width: width + 20, height: Math.max(100, height + 45) });
+
+ Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate), true);
+ Doc.ApplyTemplateTo(docTemplate, doc, undefined);
+ Doc.GetProto(dataDoc || doc).layoutCustom = Doc.MakeTitled("layoutCustom");
} else {
- doc.isButton = true;
+ swapViews(doc, "layoutCustom", "layoutNative");
}
-
- // if (doc.isButton) {
- // if (!doc.nativeWidth) {
- // doc.nativeWidth = this.props.Document[WidthSym]();
- // doc.nativeHeight = this.props.Document[HeightSym]();
- // }
- // } else {
- // doc.nativeWidth = doc.nativeHeight = undefined;
- // }
}
@undoBatch
- public fullScreenClicked = (): void => {
- CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this);
- SelectionManager.DeselectAll();
+ makeBtnClicked = (): void => {
+ if (this.Document.isButton || this.Document.onClick || this.Document.ignoreClick) {
+ this.Document.isButton = false;
+ this.Document.ignoreClick = false;
+ this.Document.onClick = undefined;
+ } else {
+ this.Document.isButton = true;
+ }
}
@undoBatch
@action
drop = async (e: Event, de: DragManager.DropEvent) => {
if (de.data instanceof DragManager.AnnotationDragData) {
+ /// this whole section for handling PDF annotations looks weird. Need to rethink this to make it cleaner
e.stopPropagation();
- let annotationDoc = de.data.annotationDocument;
- annotationDoc.linkedToDoc = true;
- de.data.targetContext = this.props.ContainingCollectionView!.props.Document;
+ let sourceDoc = de.data.annotationDocument;
let targetDoc = this.props.Document;
+ let annotations = await DocListCastAsync(sourceDoc.annotations);
+ sourceDoc.linkedToDoc = true;
+ de.data.targetContext = this.props.ContainingCollectionDoc;
targetDoc.targetContext = de.data.targetContext;
- let annotations = await DocListCastAsync(annotationDoc.annotations);
annotations && annotations.forEach(anno => anno.target = targetDoc);
- DocUtils.MakeLink(annotationDoc, targetDoc, this.props.ContainingCollectionView!.props.Document, `Link from ${StrCast(annotationDoc.title)}`);
+ DocUtils.MakeLink(sourceDoc, targetDoc, this.props.ContainingCollectionDoc, `Link from ${StrCast(sourceDoc.title)}`);
}
if (de.data instanceof DragManager.DocumentDragData && de.data.applyAsTemplate) {
- Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document, this.props.DataDoc);
+ Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document);
e.stopPropagation();
}
if (de.data instanceof DragManager.LinkDragData) {
- let sourceDoc = de.data.linkSourceDocument;
- let destDoc = this.props.Document;
-
e.stopPropagation();
- if (de.mods === "AltKey") {
- const protoDest = destDoc.proto;
- const protoSrc = sourceDoc.proto;
- let src = protoSrc ? protoSrc : sourceDoc;
- let dst = protoDest ? protoDest : destDoc;
- dst.data = (src.data! as ObjectField)[Copy]();
- dst.nativeWidth = src.nativeWidth;
- dst.nativeHeight = src.nativeHeight;
- }
- else {
- // const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true);
- // const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView);
- let linkDoc = DocUtils.MakeLink(sourceDoc, destDoc, this.props.ContainingCollectionView ? this.props.ContainingCollectionView.props.Document : undefined);
- de.data.droppedDocuments.push(destDoc);
- de.data.linkDocument = linkDoc;
- }
+ // const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true);
+ // const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView);
+ de.data.linkSourceDocument !== this.props.Document &&
+ (de.data.linkDocument = DocUtils.MakeLink(de.data.linkSourceDocument, this.props.Document, this.props.ContainingCollectionDoc));
}
}
@@ -513,9 +364,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
onDrop = (e: React.DragEvent) => {
let text = e.dataTransfer.getData("text/plain");
if (!e.isDefaultPrevented() && text && text.startsWith("<div")) {
- let oldLayout = FieldValue(this.Document.layout) || "";
+ let oldLayout = StrCast(this.props.Document.layout);
let layout = text.replace("{layout}", oldLayout);
- this.Document.layout = layout;
+ this.props.Document.layout = layout;
e.stopPropagation();
e.preventDefault();
}
@@ -524,38 +375,50 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch
@action
freezeNativeDimensions = (): void => {
- let proto = this.props.Document.isTemplate ? this.props.Document : Doc.GetProto(this.props.Document);
- this.props.Document.autoHeight = proto.autoHeight = false;
- proto.ignoreAspect = !BoolCast(proto.ignoreAspect);
- if (!BoolCast(proto.ignoreAspect) && !proto.nativeWidth) {
+ let proto = this.Document.isTemplate ? this.props.Document : Doc.GetProto(this.props.Document);
+ proto.autoHeight = this.Document.autoHeight = false;
+ proto.ignoreAspect = !proto.ignoreAspect;
+ if (!proto.ignoreAspect && !proto.nativeWidth) {
proto.nativeWidth = this.props.PanelWidth();
proto.nativeHeight = this.props.PanelHeight();
}
}
+
+ @undoBatch
+ @action
+ makeIntoPortal = async () => {
+ let anchors = await Promise.all(DocListCast(this.props.Document.links).map(async (d: Doc) => Cast(d.anchor2, Doc)));
+ if (!anchors.find(anchor2 => anchor2 && anchor2.title === this.Document.title + ".portal" ? true : false)) {
+ let portalID = (this.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, "");
+ DocServer.GetRefField(portalID).then(existingPortal => {
+ let portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { width: (this.Document.width || 0) + 10, height: this.Document.height || 0, title: portalID });
+ DocUtils.MakeLink(this.props.Document, portal, undefined, portalID);
+ this.Document.isButton = true;
+ });
+ }
+ }
+
@undoBatch
@action
- makeIntoPortal = (): void => {
- if (!DocListCast(this.props.Document.links).find(doc => {
- if (Cast(doc.anchor2, Doc) instanceof Doc && (Cast(doc.anchor2, Doc) as Doc).title === this.props.Document.title + ".portal") return true;
- return false;
- })) {
- let portal = Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".portal" });
- DocUtils.MakeLink(this.props.Document, portal, undefined, this.props.Document.title + ".portal");
- Doc.GetProto(this.props.Document).isButton = true;
+ setCustomView = (custom: boolean): void => {
+ if (this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.DataDoc) {
+ Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.ContainingCollectionView.props.DataDoc);
+ } else { // bcz: not robust -- for now documents with string layout are native documents, and those with Doc layouts are customized
+ custom ? DocumentView.makeCustomViewClicked(this.props.Document, this.props.DataDoc) : DocumentView.makeNativeViewClicked(this.props.Document);
}
}
@undoBatch
@action
makeBackground = (): void => {
- this.layoutDoc.isBackground = !this.layoutDoc.isBackground;
- this.layoutDoc.isBackground && this.props.bringToFront(this.layoutDoc, true);
+ this.Document.isBackground = !this.Document.isBackground;
+ this.Document.isBackground && this.props.bringToFront(this.Document, true);
}
@undoBatch
@action
toggleLockPosition = (): void => {
- this.layoutDoc.lockedPosition = BoolCast(this.layoutDoc.lockedPosition) ? undefined : true;
+ this.Document.lockedPosition = this.Document.lockedPosition ? undefined : true;
}
listen = async () => {
@@ -570,48 +433,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
});
}
- public static makeNativeViewClicked = undoBatch((document: Doc): void => {
- document.customLayout = document.layout;
- document.layout = document.nativeLayout;
- document.type = document.nativeType;
- document.nativeWidth = document.nativeNativeWidth;
- document.nativeHeight = document.nativeNativeHeight;
- document.ignoreAspect = document.nativeIgnoreAspect;
- document.nativeLayout = undefined;
- document.nativeNativeWidth = undefined;
- document.nativeNativeHeight = undefined;
- document.nativeIgnoreAspect = undefined;
- });
-
- public static makeCustomViewClicked = undoBatch((document: Doc, showTitle = undefined): void => {
- document.nativeLayout = document.layout;
- document.nativeType = document.type;
- document.nativeNativeWidth = document.nativeWidth;
- document.nativeNativeHeight = document.nativeHeight;
- document.nativeIgnoreAspect = document.ignoreAspect;
- PromiseValue(Cast(document.customLayout, Doc)).then(custom => {
- if (custom) {
- document.type = DocumentType.TEMPLATE;
- document.layout = custom;
- !custom.nativeWidth && (document.nativeWidth = 0);
- !custom.nativeHeight && (document.nativeHeight = 0);
- !custom.nativeWidth && (document.ignoreAspect = true);
- } else {
- let options = { title: "data", width: NumCast(document.width), height: NumCast(document.height) + 25, x: -NumCast(document.width) / 2, y: -NumCast(document.height) / 2, };
- let fieldTemplate = document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) : Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
-
- let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: StrCast(document.title) + "layout", width: NumCast(document.width) + 20, height: Math.max(100, NumCast(document.height) + 45) });
- let metaKey = "data";
- let proto = Doc.GetProto(docTemplate);
- Doc.MakeTemplate(fieldTemplate, metaKey, proto);
- fieldTemplate.showTitle = showTitle;
-
- Doc.ApplyTemplateTo(docTemplate, document, undefined, false);
- document.customLayout = document.layout;
- }
- });
- });
-
@action
onContextMenu = async (e: React.MouseEvent): Promise<void> => {
e.persist();
@@ -625,13 +446,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const cm = ContextMenu.Instance;
let subitems: ContextMenuProps[] = [];
- subitems.push({ description: "Open Full Screen", event: this.fullScreenClicked, icon: "desktop" });
- subitems.push({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.dataDoc, "inTab"), icon: "folder" });
- subitems.push({ description: "Open Tab Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "inTab"), icon: "folder" });
- subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.dataDoc, "onRight"), icon: "caret-square-right" });
- subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "onRight"), icon: "caret-square-right" });
- subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" });
+ subitems.push({ description: "Open Full Screen", event: () => CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this), icon: "desktop" });
+ subitems.push({ description: "Open Tab ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab"), icon: "folder" });
+ subitems.push({ description: "Open Right ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "onRight"), icon: "caret-square-right" });
+ subitems.push({ description: "Open Alias Tab ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "inTab"), icon: "folder" });
+ subitems.push({ description: "Open Alias Right", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "onRight"), icon: "caret-square-right" });
+ subitems.push({ description: "Open Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" });
cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" });
+
if (Cast(this.props.Document.data, ImageField)) {
cm.addItem({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" });
}
@@ -640,25 +462,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
cm.addItem({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" });
cm.addItem({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
}
- let existingMake = ContextMenu.Instance.findByDescription("Make...");
- let makes: ContextMenuProps[] = existingMake && "subitems" in existingMake ? existingMake.subitems : [];
- makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Into Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" });
- makes.push({ description: "Custom Document View", event: () => DocumentView.makeCustomViewClicked(this.props.Document), icon: "concierge-bell" });
- makes.push({ description: "Metadata Field View", event: () => this.props.ContainingCollectionView && Doc.MakeTemplate(this.props.Document, StrCast(this.props.Document.title), this.props.ContainingCollectionView.props.Document), icon: "concierge-bell" });
- makes.push({ description: "Into Portal", event: this.makeIntoPortal, icon: "window-restore" });
- makes.push({ description: this.layoutDoc.ignoreClick ? "Selectable" : "Unselectable", event: () => this.layoutDoc.ignoreClick = !this.layoutDoc.ignoreClick, icon: this.layoutDoc.ignoreClick ? "unlock" : "lock" });
- !existingMake && cm.addItem({ description: "Make...", subitems: makes, icon: "hand-point-right" });
-
let existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
let onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
- onClicks.push({ description: this.layoutDoc.ignoreClick ? "Select" : "Do Nothing", event: () => this.layoutDoc.ignoreClick = !this.layoutDoc.ignoreClick, icon: this.layoutDoc.ignoreClick ? "unlock" : "lock" });
- onClicks.push({ description: this.props.Document.isButton || this.props.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.makeBtnClicked, icon: "concierge-bell" });
+ onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript("toggleDetail(this)"), icon: "window-restore" });
+ onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
+ onClicks.push({ description: this.Document.isButton || this.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.makeBtnClicked, icon: "concierge-bell" });
onClicks.push({ description: "Edit onClick Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", obj.x, obj.y) });
onClicks.push({
description: "Edit onClick Foreach Doc Script", icon: "edit", event: (obj: any) => {
- this.props.Document.collectionContext = this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document;
+ this.props.Document.collectionContext = this.props.ContainingCollectionDoc;
ScriptBox.EditButtonScript("Foreach Collection Doc (d) => ", this.props.Document, "onClick", obj.x, obj.y, "docList(this.collectionContext.data).map(d => {", "});\n");
}
});
@@ -666,22 +480,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
let existing = ContextMenu.Instance.findByDescription("Layout...");
let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
- layoutItems.push({ description: this.props.Document.isBackground ? "As Foreground" : "As Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" });
- if (this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.layout instanceof Doc) {
- layoutItems.push({ description: "Make View of Metadata Field", event: () => this.props.ContainingCollectionView && Doc.MakeTemplate(this.props.Document, StrCast(this.props.Document.title), this.props.ContainingCollectionView.props.Document), icon: "concierge-bell" });
+ layoutItems.push({ description: this.Document.isBackground ? "As Foreground" : "As Background", event: this.makeBackground, icon: this.Document.lockedPosition ? "unlock" : "lock" });
+ if (this.props.DataDoc) {
+ layoutItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc!), icon: "concierge-bell" });
}
- layoutItems.push({ description: `${this.layoutDoc.chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.layoutDoc.chromeStatus = (this.layoutDoc.chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
- layoutItems.push({ description: `${this.layoutDoc.autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc.autoHeight = !this.layoutDoc.autoHeight, icon: "plus" });
- layoutItems.push({ description: this.props.Document.ignoreAspect || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" });
- layoutItems.push({ description: this.layoutDoc.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.layoutDoc.lockedPosition) ? "unlock" : "lock" });
+ layoutItems.push({ description: `${this.Document.chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document.chromeStatus = (this.Document.chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
+ layoutItems.push({ description: `${this.Document.autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.Document.autoHeight = !this.Document.autoHeight, icon: "plus" });
+ layoutItems.push({ description: this.Document.ignoreAspect || !this.Document.nativeWidth || !this.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" });
+ layoutItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
layoutItems.push({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" });
layoutItems.push({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" });
- if (this.props.Document.detailedLayout && !this.props.Document.isTemplate) {
- layoutItems.push({ description: "Toggle detail", event: () => Doc.ToggleDetailLayout(this.props.Document), icon: "image" });
- }
- if (this.props.Document.type !== DocumentType.COL && this.props.Document.type !== DocumentType.TEMPLATE) {
- layoutItems.push({ description: "Use Custom Layout", event: () => DocumentView.makeCustomViewClicked(this.props.Document), icon: "concierge-bell" });
- } else if (this.props.Document.nativeLayout) {
+ if (this.Document.type !== DocumentType.COL && this.Document.type !== DocumentType.TEMPLATE) {
+ layoutItems.push({ description: "Use Custom Layout", event: () => DocumentView.makeCustomViewClicked(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
+ } else if (this.props.Document.layoutNative) {
layoutItems.push({ description: "Use Native Layout", event: () => DocumentView.makeNativeViewClicked(this.props.Document), icon: "concierge-bell" });
}
!existing && cm.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" });
@@ -697,19 +508,18 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
cm.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin" }); //I think this should work... and it does! A miracle!
cm.addItem({ description: "Add Repl", icon: "laptop-code", event: () => OverlayView.Instance.addWindow(<ScriptingRepl />, { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" }) });
cm.addItem({
- description: "Download document", icon: "download", event: async () => {
- let y = JSON.parse(await rp.get(Utils.CorsProxy("http://localhost:8983/solr/dash/select"), {
+ description: "Download document", icon: "download", event: async () =>
+ console.log(JSON.parse(await rp.get(Utils.CorsProxy("http://localhost:8983/solr/dash/select"), {
qs: { q: 'world', fq: 'NOT baseProto_b:true AND NOT deleted:true', start: '0', rows: '100', hl: true, 'hl.fl': '*' }
- }));
- console.log(y);
- // const a = document.createElement("a");
- // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`);
- // a.href = url;
- // a.download = `DocExport-${this.props.Document[Id]}.zip`;
- // a.click();
- }
+ })))
+ // const a = document.createElement("a");
+ // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`);
+ // a.href = url;
+ // a.download = `DocExport-${this.props.Document[Id]}.zip`;
+ // a.click();
});
+ cm.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, this.Document.title || "", this.props.addDocument, this.props.removeDocument), icon: "file" });
cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" });
runInAction(() => {
if (!ClientUtils.RELEASE) {
@@ -754,80 +564,90 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
});
}
- onPointerEnter = (e: React.PointerEvent): void => { Doc.BrushDoc(this.props.Document); };
- onPointerLeave = (e: React.PointerEvent): void => { Doc.UnBrushDoc(this.props.Document); };
- isSelected = () => SelectionManager.IsSelected(this);
- @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); };
- @computed get nativeWidth() { return this.Document.nativeWidth || 0; }
- @computed get nativeHeight() { return this.Document.nativeHeight || 0; }
- @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : this.Document.onClick; }
- @computed get contents() {
- return (<DocumentContentsView {...this.props}
- ChromeHeight={this.chromeHeight}
- isSelected={this.isSelected}
- select={this.select}
- onClick={this.onClickHandler}
- layoutKey={"layout"}
- fitToBox={BoolCast(this.props.Document.fitToBox) ? true : this.props.fitToBox}
- DataDoc={this.dataDoc} />);
+ // the document containing the view layout information - will be the Document itself unless the Document has
+ // a layout field. In that case, all layout information comes from there unless overriden by Document
+ get layoutDoc(): Document {
+ return Document(this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document);
}
- chromeHeight = () => {
- let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.layoutDoc) : undefined;
- let showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.layoutDoc.showTitle);
- let templates = Cast(this.layoutDoc.templates, listSpec("string"));
- if (!showOverlays && templates instanceof List) {
- templates.map(str => {
- if (!showTitle && str.indexOf("{props.Document.title}") !== -1) showTitle = "title";
- });
- }
- return (showTitle ? 25 : 0) + 1;// bcz: why 8??
- }
+ // does Document set a layout prop
+ setsLayoutProp = (prop: string) => this.props.Document[prop] !== this.props.Document["default" + prop[0].toUpperCase() + prop.slice(1)];
+ // get the a layout prop by first choosing the prop from Document, then falling back to the layout doc otherwise.
+ getLayoutPropStr = (prop: string) => StrCast(this.setsLayoutProp(prop) ? this.props.Document[prop] : this.layoutDoc[prop]);
+ getLayoutPropNum = (prop: string) => NumCast(this.setsLayoutProp(prop) ? this.props.Document[prop] : this.layoutDoc[prop]);
- get layoutDoc() {
- // if this document's layout field contains a document (ie, a rendering template), then we will use that
- // to determine the render JSX string, otherwise the layout field should directly contain a JSX layout string.
- return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
- }
+ isSelected = () => SelectionManager.IsSelected(this);
+ select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); };
+ chromeHeight = () => {
+ let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined;
+ let showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.Document.showTitle);
+ return (showTitle ? 25 : 0) + 1;
+ }
render() {
- let backgroundColor = this.layoutDoc.isBackground || (this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.clusterOverridesDefaultBackground && this.layoutDoc.backgroundColor === this.layoutDoc.defaultBackgroundColor) ?
- this.props.backgroundColor(this.layoutDoc) || StrCast(this.layoutDoc.backgroundColor) :
- StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.layoutDoc);
- let foregroundColor = StrCast(this.layoutDoc.color);
- var nativeWidth = this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? `${this.nativeWidth}px` : "100%";
- var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
- let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.layoutDoc) : undefined;
- let showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.layoutDoc.showTitle);
- let showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : StrCast(this.layoutDoc.showCaption);
- let templates = Cast(this.layoutDoc.templates, listSpec("string"));
- if (!showOverlays && templates instanceof List) {
- templates.map(str => {
- if (!showTitle && str.indexOf("{props.Document.title}") !== -1) showTitle = "title";
- if (!showCaption && str.indexOf("fieldKey={\"caption\"}") !== -1) showCaption = "caption";
- });
- }
- let showTextTitle = showTitle && StrCast(this.layoutDoc.layout).startsWith("<FormattedTextBox") ? showTitle : undefined;
- let fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
- let borderRounding = StrCast(Doc.GetProto(this.props.Document).borderRounding);
- let localScale = this.props.ScreenToLocalTransform().Scale * fullDegree;
- let searchHighlight = (!this.props.Document.search_fields ? (null) :
- <div key="search" style={{ position: "absolute", background: "yellow", bottom: "-20px", borderRadius: "5px", transformOrigin: "bottom left", width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
- {StrCast(this.props.Document.search_fields)}
+ const ruleColor = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleColor_" + this.Document.heading]) : undefined;
+ const ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
+ const colorSet = this.setsLayoutProp("backgroundColor");
+ const clusterCol = this.props.ContainingCollectionDoc && this.props.ContainingCollectionDoc.clusterOverridesDefaultBackground;
+ const backgroundColor = this.Document.isBackground || (clusterCol && !colorSet) ?
+ this.props.backgroundColor(this.Document) || StrCast(this.layoutDoc.backgroundColor) :
+ ruleColor && !colorSet ? ruleColor : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.Document);
+
+ const nativeWidth = this.nativeWidth > 0 && !this.Document.ignoreAspect ? `${this.nativeWidth}px` : "100%";
+ const nativeHeight = this.Document.ignoreAspect ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
+ const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined;
+ const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : this.getLayoutPropStr("showTitle");
+ const showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : this.getLayoutPropStr("showCaption");
+ const showTextTitle = showTitle && StrCast(this.Document.layout).indexOf("FormattedTextBox") !== -1 ? showTitle : undefined;
+ const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
+ const borderRounding = this.getLayoutPropStr("borderRounding") || ruleRounding;
+ const localScale = this.props.ScreenToLocalTransform().Scale * fullDegree;
+ const searchHighlight = (!this.Document.searchFields ? (null) :
+ <div className="documentView-searchHighlight" style={{ width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
+ {this.Document.searchFields}
+ </div>);
+ const captionView = (!showCaption ? (null) :
+ <div className="documentView-captionWrapper" style={{ width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
+ <FormattedTextBox {...this.props}
+ onClick={this.onClickHandler} DataDoc={this.props.DataDoc} active={returnTrue}
+ isSelected={this.isSelected} focus={emptyFunction} select={this.select}
+ fieldExt={""} hideOnLeave={true} fieldKey={showCaption}
+ />
+ </div>);
+ const titleView = (!showTitle ? (null) :
+ <div className="documentView-titleWrapper" style={{
+ position: showTextTitle ? "relative" : "absolute",
+ pointerEvents: SelectionManager.GetIsDragging() ? "none" : "all",
+ width: `${100 * this.props.ContentScaling()}%`,
+ transform: `scale(${1 / this.props.ContentScaling()})`
+ }}>
+ <EditableView
+ contents={this.Document[showTitle]}
+ display={"block"} height={72} fontSize={12}
+ GetValue={() => StrCast(this.Document[showTitle])}
+ SetValue={(value: string) => (Doc.GetProto(this.Document)[showTitle] = value) ? true : true}
+ />
</div>);
+ const contents = (<DocumentContentsView {...this.props}
+ ChromeHeight={this.chromeHeight}
+ isSelected={this.isSelected}
+ select={this.select}
+ onClick={this.onClickHandler}
+ layoutKey={"layout"}
+ DataDoc={this.props.DataDoc} />);
return (
<div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
ref={this._mainCont}
style={{
- pointerEvents: this.layoutDoc.isBackground && !this.isSelected() ? "none" : "all",
- color: foregroundColor,
+ transition: this.props.Document.isAnimating !== undefined ? ".5s linear" : StrCast(this.Document.transition),
+ pointerEvents: this.Document.isBackground && !this.isSelected() ? "none" : "all",
+ color: StrCast(this.Document.color),
outlineColor: ["transparent", "maroon", "maroon", "yellow"][fullDegree],
outlineStyle: ["none", "dashed", "solid", "solid"][fullDegree],
outlineWidth: fullDegree && !borderRounding ? `${localScale}px` : "0px",
border: fullDegree && borderRounding ? `${["none", "dashed", "solid", "solid"][fullDegree]} ${["transparent", "maroon", "maroon", "yellow"][fullDegree]} ${localScale}px` : undefined,
- borderRadius: "inherit",
background: backgroundColor,
width: nativeWidth,
height: nativeHeight,
@@ -835,44 +655,60 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
opacity: this.Document.opacity
}}
onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
- onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}
+ onPointerEnter={() => Doc.BrushDoc(this.props.Document)} onPointerLeave={() => Doc.UnBrushDoc(this.props.Document)}
>
{!showTitle && !showCaption ?
- this.props.Document.search_fields ? <div>
- {this.contents}
- {searchHighlight}
- </div> :
- this.contents :
- <div style={{ position: "absolute", display: "inline-block", width: "100%", height: "100%", pointerEvents: "none" }}>
- <div style={{ width: "100%", height: showTextTitle ? "calc(100% - 29px)" : "100%", display: "inline-block", position: "absolute", top: showTextTitle ? "29px" : undefined }}>
- {this.contents}
+ this.Document.searchFields ?
+ (<div className="documentView-searchWrapper">
+ {contents}
+ {searchHighlight}
+ </div>)
+ :
+ contents
+ :
+ <div className="documentView-styleWrapper" >
+ <div className="documentView-styleContentWrapper" style={{ height: showTextTitle ? "calc(100% - 29px)" : "100%", top: showTextTitle ? "29px" : undefined }}>
+ {contents}
</div>
- {!showTitle ? (null) :
- <div style={{
- position: showTextTitle ? "relative" : "absolute", top: 0, padding: "4px", textAlign: "center", textOverflow: "ellipsis", whiteSpace: "pre",
- pointerEvents: SelectionManager.GetIsDragging() ? "none" : "all",
- overflow: "hidden", width: `${100 * this.props.ContentScaling()}%`, height: 25, background: "rgba(0, 0, 0, .4)", color: "white",
- transformOrigin: "top left", transform: `scale(${1 / this.props.ContentScaling()})`
- }}>
- <EditableView
- contents={(this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[showTitle]}
- display={"block"}
- height={72}
- fontSize={12}
- GetValue={() => StrCast((this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[showTitle!])}
- SetValue={(value: string) => ((this.layoutDoc.isTemplate ? this.layoutDoc : Doc.GetProto(this.layoutDoc))[showTitle!] = value) ? true : true}
- />
- </div>
- }
- {!showCaption ? (null) :
- <div style={{ position: "absolute", bottom: 0, transformOrigin: "bottom left", width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
- <FormattedTextBox {...this.props} onClick={this.onClickHandler} DataDoc={this.dataDoc} active={returnTrue} isSelected={this.isSelected} focus={emptyFunction} select={this.select} fieldExt={""} hideOnLeave={true} fieldKey={showCaption} />
- </div>
- }
+ {titleView}
+ {captionView}
{searchHighlight}
</div>
}
</div>
);
}
-} \ No newline at end of file
+}
+
+export async function swapViews(doc: Doc, newLayoutField: string, oldLayoutField: string, oldLayout?: Doc) {
+ let oldLayoutExt = oldLayout || await Cast(doc[oldLayoutField], Doc);
+ if (oldLayoutExt) {
+ oldLayoutExt.autoHeight = doc.autoHeight;
+ oldLayoutExt.width = doc.width;
+ oldLayoutExt.height = doc.height;
+ oldLayoutExt.nativeWidth = doc.nativeWidth;
+ oldLayoutExt.nativeHeight = doc.nativeHeight;
+ oldLayoutExt.ignoreAspect = doc.ignoreAspect;
+ oldLayoutExt.backgroundLayout = doc.backgroundLayout;
+ oldLayoutExt.type = doc.type;
+ oldLayoutExt.layout = doc.layout;
+ }
+
+ let newLayoutExt = newLayoutField && await Cast(doc[newLayoutField], Doc);
+ if (newLayoutExt) {
+ doc.autoHeight = newLayoutExt.autoHeight;
+ doc.width = newLayoutExt.width;
+ doc.height = newLayoutExt.height;
+ doc.nativeWidth = newLayoutExt.nativeWidth;
+ doc.nativeHeight = newLayoutExt.nativeHeight;
+ doc.ignoreAspect = newLayoutExt.ignoreAspect;
+ doc.backgroundLayout = newLayoutExt.backgroundLayout;
+ doc.type = newLayoutExt.type;
+ doc.layout = await newLayoutExt.layout;
+ }
+}
+
+Scripting.addGlobal(function toggleDetail(doc: any) {
+ let native = typeof doc.layout === "string";
+ swapViews(doc, native ? "layoutCustom" : "layoutNative", native ? "layoutNative" : "layoutCustom");
+}); \ No newline at end of file
diff --git a/src/client/views/nodes/DragBox.tsx b/src/client/views/nodes/DragBox.tsx
index 1f2c88086..6c3db18c4 100644
--- a/src/client/views/nodes/DragBox.tsx
+++ b/src/client/views/nodes/DragBox.tsx
@@ -45,17 +45,15 @@ export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocu
}
onDragMove = (e: MouseEvent) => {
- if (!e.cancelBubble && !this.props.Document.excludeFromLibrary && (Math.abs(this._downX - e.clientX) > 5 || Math.abs(this._downY - e.clientY) > 5)) {
+ if (!e.cancelBubble && (Math.abs(this._downX - e.clientX) > 5 || Math.abs(this._downY - e.clientY) > 5)) {
document.removeEventListener("pointermove", this.onDragMove);
document.removeEventListener("pointerup", this.onDragUp);
const onDragStart = this.Document.onDragStart;
e.stopPropagation();
e.preventDefault();
- let res = onDragStart ? onDragStart.script.run({ this: this.props.Document }) : undefined;
- let doc = res !== undefined && res.success ?
- res.result as Doc :
- Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" });
- doc && DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([doc], [undefined]), e.clientX, e.clientY);
+ let res = onDragStart && onDragStart.script.run({ this: this.props.Document }).result;
+ let doc = (res as Doc) || Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" });
+ DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY);
}
e.stopPropagation();
e.preventDefault();
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index d9774303b..49fc2263d 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -30,6 +30,8 @@ export interface FieldViewProps {
leaveNativeSize?: boolean;
fitToBox?: boolean;
ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
+ ContainingCollectionDoc: Opt<Doc>;
+ ruleProvider: Doc | undefined;
Document: Doc;
DataDoc?: Doc;
onClick?: ScriptField;
@@ -37,7 +39,7 @@ export interface FieldViewProps {
select: (isCtrlPressed: boolean) => void;
renderDepth: number;
addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean;
- addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+ addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
pinToPres: (document: Doc) => void;
removeDocument?: (document: Doc) => boolean;
moveDocument?: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index d7ac7a9c5..0d7277cbe 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -4,7 +4,6 @@
width: 100%;
height: 100%;
min-height: 100%;
- font-family: $serif;
}
.ProseMirror:focus {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 8f0f142c4..cb9fecfc5 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -41,7 +41,7 @@ import { RichTextUtils } from '../../../new_fields/RichTextUtils';
import * as _ from "lodash";
import { formattedTextBoxCommentPlugin, FormattedTextBoxComment } from './FormattedTextBoxComment';
import { inputRules } from 'prosemirror-inputrules';
-import { select } from 'async';
+import { DocumentButtonBar } from '../DocumentButtonBar';
library.add(faEdit);
library.add(faSmile, faTextHeight, faUpload);
@@ -81,15 +81,19 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
private _linkClicked = "";
private _nodeClicked: any;
private _undoTyping?: UndoManager.Batch;
- private _reactionDisposer: Opt<IReactionDisposer>;
private _searchReactionDisposer?: Lambda;
+ private _reactionDisposer: Opt<IReactionDisposer>;
private _textReactionDisposer: Opt<IReactionDisposer>;
private _heightReactionDisposer: Opt<IReactionDisposer>;
+ private _rulesReactionDisposer: Opt<IReactionDisposer>;
private _proxyReactionDisposer: Opt<IReactionDisposer>;
private _pullReactionDisposer: Opt<IReactionDisposer>;
private _pushReactionDisposer: Opt<IReactionDisposer>;
private dropDisposer?: DragManager.DragDropDisposer;
+ @observable private _fontSize = 13;
+ @observable private _fontFamily = "Arial";
+ @observable private _fontAlign = "";
@observable private _entered = false;
@observable public static InputBoxOverlay?: FormattedTextBox = undefined;
public static SelectOnLoad = "";
@@ -121,14 +125,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
@undoBatch
public setFontColor(color: string) {
- this._editorView!.state.storedMarks;
- if (this._editorView!.state.selection.from === this._editorView!.state.selection.to) return false;
- if (this._editorView!.state.selection.to - this._editorView!.state.selection.from > this._editorView!.state.doc.nodeSize - 3) {
+ let view = this._editorView!;
+ if (view.state.selection.from === view.state.selection.to) return false;
+ if (view.state.selection.to - view.state.selection.from > view.state.doc.nodeSize - 3) {
this.props.Document.color = color;
}
- let colorMark = this._editorView!.state.schema.mark(this._editorView!.state.schema.marks.pFontColor, { color: color });
- this._editorView!.dispatch(this._editorView!.state.tr.addMark(this._editorView!.state.selection.from,
- this._editorView!.state.selection.to, colorMark));
+ let colorMark = view.state.schema.mark(view.state.schema.marks.pFontColor, { color: color });
+ view.dispatch(view.state.tr.addMark(view.state.selection.from, view.state.selection.to, colorMark));
return true;
}
@@ -142,10 +145,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
public get CurrentDiv(): HTMLDivElement { return this._ref.current!; }
- @computed get extensionDoc() { return Doc.resolvedFieldDataDoc(this.dataDoc, this.props.fieldKey, "dummy"); }
-
- @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
+ @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
+ @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
// this should be internal to prosemirror, but is needed
// here to make sure that footnote view nodes in the overlay editor
@@ -170,6 +172,25 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
}
+ linkOnDeselect: Map<string, string> = new Map();
+
+ doLinkOnDeselect() {
+ Array.from(this.linkOnDeselect.entries()).map(entry => {
+ let key = entry[0];
+ let value = entry[1];
+ let id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
+ DocServer.GetRefField(value).then(doc => {
+ DocServer.GetRefField(id).then(linkDoc => {
+ this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value);
+ DocUtils.Publish(this.dataDoc[key] as Doc, value, this.props.addDocument, this.props.removeDocument);
+ if (linkDoc) { (linkDoc as Doc).anchor2 = this.dataDoc[key] as Doc; }
+ else DocUtils.MakeLink(this.dataDoc, this.dataDoc[key] as Doc, undefined, "Ref:" + value, undefined, undefined, id, true);
+ });
+ });
+ });
+ this.linkOnDeselect.clear();
+ }
+
dispatchTransaction = (tx: Transaction) => {
if (this._editorView) {
let metadata = tx.selection.$from.marks().find((m: Mark) => m.type === schema.marks.metadata);
@@ -184,15 +205,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (split.length > 1 && split[1]) {
let key = split[0];
let value = split[split.length - 1];
+ this.linkOnDeselect.set(key, value);
let id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
- DocServer.GetRefField(value).then(doc => {
- DocServer.GetRefField(id).then(linkDoc => {
- this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value);
- if (linkDoc) { (linkDoc as Doc).anchor2 = this.dataDoc[key] as Doc; }
- else DocUtils.MakeLink(this.dataDoc, this.dataDoc[key] as Doc, undefined, "Ref:" + value, undefined, undefined, id);
- });
- });
const link = this._editorView.state.schema.marks.link.create({ href: `http://localhost:1050/doc/${id}`, location: "onRight", title: value });
const mval = this._editorView.state.schema.marks.metadataVal.create();
let offset = (tx.selection.to === range!.end - 1 ? -1 : 0);
@@ -256,12 +271,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
}
});
- // const fieldkey = 'search_string';
- // if (Object.keys(this.props.Document).indexOf(fieldkey) !== -1) {
- // this.props.Document[fieldkey] = undefined;
- // }
- // else this.props.Document.proto![fieldkey] = undefined;
- // }
}
}
setAnnotation = (start: number, end: number, mark: Mark, opened: boolean, keep: boolean = false) => {
@@ -292,16 +301,29 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
e.stopPropagation();
} else if (de.data instanceof DragManager.DocumentDragData) {
const draggedDoc = de.data.draggedDocuments.length && de.data.draggedDocuments[0];
- if (draggedDoc && draggedDoc.type === DocumentType.TEXT && StrCast(draggedDoc.layout) !== "") {
- this.props.Document.layout = draggedDoc;
- draggedDoc.isTemplate = true;
+ if (draggedDoc && draggedDoc.type === DocumentType.TEXT && !Doc.AreProtosEqual(draggedDoc, this.props.Document)) {
+ if (de.mods === "AltKey") {
+ if (draggedDoc.data instanceof RichTextField) {
+ Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new RichTextField(draggedDoc.data.Data);
+ e.stopPropagation();
+ }
+ } else {
+ draggedDoc.isTemplate = true;
+ if (typeof (draggedDoc.layout) === "string") {
+ let layoutDelegateToOverrideFieldKey = Doc.MakeDelegate(draggedDoc);
+ layoutDelegateToOverrideFieldKey.layout = StrCast(layoutDelegateToOverrideFieldKey.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${this.props.fieldKey}"}`);
+ this.props.Document.layout = layoutDelegateToOverrideFieldKey;
+ } else {
+ this.props.Document.layout = draggedDoc.layout instanceof Doc ? draggedDoc.layout : draggedDoc;
+ }
+ }
e.stopPropagation();
}
}
}
recordKeyHandler = (e: KeyboardEvent) => {
- if (this.props.Document === SelectionManager.SelectedDocuments()[0].props.Document) {
+ if (SelectionManager.SelectedDocuments().length && this.props.Document === SelectionManager.SelectedDocuments()[0].props.Document) {
if (e.key === "R" && e.altKey) {
e.stopPropagation();
e.preventDefault();
@@ -380,6 +402,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
componentDidMount() {
+
if (!this.props.isOverlay) {
this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
() => {
@@ -410,8 +433,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
this._pullReactionDisposer = reaction(
() => this.props.Document[Pulls],
() => {
- if (!DocumentDecorations.hasPulledHack) {
- DocumentDecorations.hasPulledHack = true;
+ if (!DocumentButtonBar.hasPulledHack) {
+ DocumentButtonBar.hasPulledHack = true;
let unchanged = this.dataDoc.unchanged;
this.pullFromGoogleDoc(unchanged ? this.checkState : this.updateState);
}
@@ -421,8 +444,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
this._pushReactionDisposer = reaction(
() => this.props.Document[Pushes],
() => {
- if (!DocumentDecorations.hasPushedHack) {
- DocumentDecorations.hasPushedHack = true;
+ if (!DocumentButtonBar.hasPushedHack) {
+ DocumentButtonBar.hasPushedHack = true;
this.pushToGoogleDoc();
}
}
@@ -462,6 +485,39 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
this.unhighlightSearchTerms();
}
}, { fireImmediately: true });
+
+
+ this._rulesReactionDisposer = reaction(() => {
+ let ruleProvider = this.props.ruleProvider;
+ let heading = NumCast(this.props.Document.heading);
+ if (ruleProvider instanceof Doc) {
+ return {
+ align: StrCast(ruleProvider["ruleAlign_" + heading], ""),
+ font: StrCast(ruleProvider["ruleFont_" + heading], "Arial"),
+ size: NumCast(ruleProvider["ruleSize_" + heading], 13)
+ };
+ }
+ return undefined;
+ },
+ action((rules: any) => {
+ this._fontFamily = rules ? rules.font : "Arial";
+ this._fontSize = rules ? rules.size : 13;
+ rules && setTimeout(() => {
+ const view = this._editorView!;
+ if (this._proseRef) {
+ let n = new NodeSelection(view.state.doc.resolve(0));
+ if (this._editorView!.state.doc.textContent === "") {
+ view.dispatch(view.state.tr.setSelection(new TextSelection(view.state.doc.resolve(0), view.state.doc.resolve(2))).
+ replaceSelectionWith(this._editorView!.state.schema.nodes.paragraph.create({ align: rules.align }), true));
+ } else if (n.node && n.node.type === view.state.schema.nodes.paragraph) {
+ view.dispatch(view.state.tr.setNodeMarkup(0, n.node.type, { ...n.node.attrs, align: rules.align }));
+ }
+ this.tryUpdateHeight();
+ }
+ }, 0);
+ }), { fireImmediately: true }
+ );
+
setTimeout(() => this.tryUpdateHeight(), 0);
}
@@ -481,7 +537,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
response && (this.dataDoc[GoogleRef] = response.documentId);
let pushSuccess = response !== undefined && !("errors" in response);
dataDoc.unchanged = pushSuccess;
- DocumentDecorations.Instance.startPushOutcome(pushSuccess);
+ DocumentButtonBar.Instance.startPushOutcome(pushSuccess);
}
};
let undo = () => {
@@ -529,7 +585,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
} else {
delete dataDoc[GoogleRef];
}
- DocumentDecorations.Instance.startPullOutcome(pullSuccess);
+ DocumentButtonBar.Instance.startPullOutcome(pullSuccess);
}
checkState = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => {
@@ -538,7 +594,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
let equalTitles = dataDoc.title === exportState.title;
let unchanged = equalContent && equalTitles;
dataDoc.unchanged = unchanged;
- DocumentDecorations.Instance.setPullState(unchanged);
+ DocumentButtonBar.Instance.setPullState(unchanged);
}
}
@@ -585,7 +641,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
let annotations = DocListCast(region.annotations);
annotations.forEach(anno => anno.target = this.props.Document);
- let fieldExtDoc = Doc.resolvedFieldDataDoc(doc, "data", "true");
+ let fieldExtDoc = Doc.fieldExtensionDoc(doc, "data");
let targetAnnotations = DocListCast(fieldExtDoc.annotations);
if (targetAnnotations) {
targetAnnotations.push(region);
@@ -666,27 +722,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
else if (this.props.isOverlay) this._editorView!.focus();
// add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
this._editorView!.state.storedMarks = [...(this._editorView!.state.storedMarks ? this._editorView!.state.storedMarks : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })];
- let heading = this.props.Document.heading;
- if (heading && selectOnLoad) {
- PromiseValue(Cast(this.props.Document.ruleProvider, Doc)).then(ruleProvider => {
- if (ruleProvider) {
- let align = StrCast(ruleProvider["ruleAlign_" + heading]);
- let font = StrCast(ruleProvider["ruleFont_" + heading]);
- let size = NumCast(ruleProvider["ruleSize_" + heading]);
- if (align) {
- let tr = this._editorView!.state.tr;
- tr = tr.setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(2))).
- replaceSelectionWith(this._editorView!.state.schema.nodes.paragraph.create({ align: align }), true).
- setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(0)));
- this._editorView!.dispatch(tr);
- }
- let sm = [...(this._editorView!.state.storedMarks ? this._editorView!.state.storedMarks : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })];
- size && (sm = [...sm, schema.marks.pFontSize.create({ fontSize: size })]);
- font && (sm = [...sm, this.getFont(font)]);
- this._editorView!.dispatch(this._editorView!.state.tr.setStoredMarks(sm));
- }
- });
- }
}
getFont(font: string) {
switch (font) {
@@ -702,6 +737,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
componentWillUnmount() {
+ this._rulesReactionDisposer && this._rulesReactionDisposer();
this._reactionDisposer && this._reactionDisposer();
this._proxyReactionDisposer && this._proxyReactionDisposer();
this._textReactionDisposer && this._textReactionDisposer();
@@ -857,6 +893,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
this._undoTyping.end();
this._undoTyping = undefined;
}
+ this.doLinkOnDeselect();
}
onKeyPress = (e: React.KeyboardEvent) => {
if (e.key === "Escape") {
@@ -866,7 +903,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (e.key === "Tab" || e.key === "Enter") {
e.preventDefault();
}
- this._editorView!.state.tr.addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() }));
+ this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() }));
if (!this._undoTyping) {
this._undoTyping = UndoManager.StartBatch("undoTyping");
@@ -877,7 +914,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
tryUpdateHeight() {
const ChromeHeight = this.props.ChromeHeight;
let sh = this._ref.current ? this._ref.current.scrollHeight : 0;
- if (!this.props.isOverlay && this.props.Document.autoHeight && sh !== 0) {
+ if (!this.props.isOverlay && !this.props.Document.isAnimating && this.props.Document.autoHeight && sh !== 0) {
let nh = this.props.Document.isTemplate ? 0 : NumCast(this.dataDoc.nativeHeight, 0);
let dh = NumCast(this.props.Document.height, 0);
this.props.Document.height = Math.max(10, (nh ? dh / nh * sh : sh) + (ChromeHeight ? ChromeHeight() : 0));
@@ -885,12 +922,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
}
-
render() {
let style = this.props.isOverlay ? "scroll" : "hidden";
let rounded = StrCast(this.props.Document.borderRounding) === "100%" ? "-rounded" : "";
- let interactive: "all" | "none" = InkingControl.Instance.selectedTool || this.props.Document.isBackground ||
- (this.props.Document.isButton && !this.props.isSelected()) ? "none" : "all";
+ let interactive: "all" | "none" = InkingControl.Instance.selectedTool || this.props.Document.isBackground
+ ? "none" : "all";
Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
return (
<div className={`formattedTextBox-cont-${style}`} ref={this._ref}
@@ -901,7 +937,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
opacity: this.props.hideOnLeave ? (this._entered || this.props.isSelected() || Doc.IsBrushed(this.props.Document) ? 1 : 0.1) : 1,
color: this.props.color ? this.props.color : this.props.hideOnLeave ? "white" : "inherit",
pointerEvents: interactive,
- fontSize: "13px"
+ fontSize: this._fontSize,
+ fontFamily: this._fontFamily,
}}
onKeyDown={this.onKeyPress}
onFocus={this.onFocused}
@@ -914,7 +951,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
onPointerEnter={action(() => this._entered = true)}
onPointerLeave={action(() => this._entered = false)}
>
- <div className={`formattedTextBox-inner${rounded}`} ref={this.createDropTarget} style={{ whiteSpace: "pre-wrap" }} />
+ <div className={`formattedTextBox-inner${rounded}`} style={{ whiteSpace: "pre-wrap", pointerEvents: ((this.props.Document.isButton || this.props.onClick) && !this.props.isSelected()) ? "none" : undefined }} ref={this.createDropTarget} />
</div>
);
}
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
index 7e78ec684..63a504d1a 100644
--- a/src/client/views/nodes/IconBox.tsx
+++ b/src/client/views/nodes/IconBox.tsx
@@ -12,6 +12,8 @@ import { IconField } from "../../../new_fields/IconField";
import { ContextMenu } from "../ContextMenu";
import Measure from "react-measure";
import { MINIMIZED_ICON_SIZE } from "../../views/globalCssVariables.scss";
+import { Scripting } from "../../util/Scripting";
+import { ComputedField } from "../../../new_fields/ScriptField";
library.add(faCaretUp);
@@ -27,6 +29,25 @@ export class IconBox extends React.Component<FieldViewProps> {
@computed get layout(): string { const field = Cast(this.props.Document[this.props.fieldKey], IconField); return field ? field.icon : "<p>Error loading icon data</p>"; }
@computed get minimizedIcon() { return IconBox.DocumentIcon(this.layout); }
+ public static summaryTitleScript(inputDoc: Doc) {
+ const sumDoc = Cast(inputDoc.summaryDoc, Doc) as Doc;
+ if (sumDoc && StrCast(sumDoc.title).startsWith("-")) {
+ return sumDoc.title + ".expanded";
+ }
+ return "???";
+ }
+ public static titleScript(inputDoc: Doc) {
+ const maxDoc = DocListCast(inputDoc.maximizedDocs);
+ if (maxDoc.length === 1) {
+ return maxDoc[0].title + ".icon";
+ }
+ return maxDoc.length > 1 ? "-multiple-.icon" : "???";
+ }
+
+ public static AutomaticTitle(doc: Doc) {
+ Doc.GetProto(doc).title = ComputedField.MakeFunction('iconTitle(this);');
+ }
+
public static DocumentIcon(layout: string) {
let button = layout.indexOf("PDFBox") !== -1 ? faFilePdf :
layout.indexOf("ImageBox") !== -1 ? faImage :
@@ -38,35 +59,20 @@ export class IconBox extends React.Component<FieldViewProps> {
}
setLabelField = (): void => {
- this.props.Document.hideLabel = !BoolCast(this.props.Document.hideLabel);
- }
- setUseOwnTitleField = (): void => {
- this.props.Document.useOwnTitle = !BoolCast(this.props.Document.useTargetTitle);
+ this.props.Document.hideLabel = !this.props.Document.hideLabel;
}
specificContextMenu = (): void => {
- ContextMenu.Instance.addItem({
- description: BoolCast(this.props.Document.hideLabel) ? "Show label with icon" : "Remove label from icon",
- event: this.setLabelField,
- icon: "tag"
- });
- let maxDocs = DocListCast(this.props.Document.maximizedDocs);
- if (maxDocs.length === 1 && !BoolCast(this.props.Document.hideLabel)) {
- ContextMenu.Instance.addItem({
- description: BoolCast(this.props.Document.useOwnTitle) ? "Use target title for label" : "Use own title label",
- event: this.setUseOwnTitleField,
- icon: "text-height"
- });
+ let cm = ContextMenu.Instance;
+ cm.addItem({ description: this.props.Document.hideLabel ? "Show label with icon" : "Remove label from icon", event: this.setLabelField, icon: "tag" });
+ if (!this.props.Document.hideLabel) {
+ cm.addItem({ description: "Use Target Title", event: () => IconBox.AutomaticTitle(this.props.Document), icon: "text-height" });
}
}
@observable _panelWidth: number = 0;
@observable _panelHeight: number = 0;
render() {
- let labelField = StrCast(this.props.Document.labelField);
- let hideLabel = BoolCast(this.props.Document.hideLabel);
- let maxDocs = DocListCast(this.props.Document.maximizedDocs);
- let firstDoc = maxDocs.length ? maxDocs[0] : undefined;
- let label = hideLabel ? "" : (firstDoc && labelField && !BoolCast(this.props.Document.useOwnTitle) ? firstDoc[labelField] : this.props.Document.title);
+ let label = this.props.Document.hideLabel ? "" : this.props.Document.title;
return (
<div className="iconBox-container" onContextMenu={this.specificContextMenu}>
{this.minimizedIcon}
@@ -82,4 +88,6 @@ export class IconBox extends React.Component<FieldViewProps> {
</Measure>
</div>);
}
-} \ No newline at end of file
+}
+Scripting.addGlobal(function iconTitle(doc: any) { return IconBox.titleScript(doc); });
+Scripting.addGlobal(function summaryTitle(doc: any) { return IconBox.summaryTitleScript(doc); }); \ No newline at end of file
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 649d2d056..1645c4ffd 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -17,13 +17,12 @@ import { Utils } from '../../../Utils';
import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
-import { CompileScript } from '../../util/Scripting';
import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from "../../views/ContextMenu";
import { ContextMenuProps } from '../ContextMenuItem';
import { DocComponent } from '../DocComponent';
import { InkingControl } from '../InkingControl';
-import { positionSchema } from './DocumentView';
+import { documentSchema } from './DocumentView';
import FaceRectangles from './FaceRectangles';
import { FieldView, FieldViewProps } from './FieldView';
import "./ImageBox.scss";
@@ -50,8 +49,8 @@ declare class MediaRecorder {
constructor(e: any);
}
-type ImageDocument = makeInterface<[typeof pageSchema, typeof positionSchema]>;
-const ImageDocument = makeInterface(pageSchema, positionSchema);
+type ImageDocument = makeInterface<[typeof pageSchema, typeof documentSchema]>;
+const ImageDocument = makeInterface(pageSchema, documentSchema);
@observer
export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageDocument) {
@@ -65,7 +64,9 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
private dropDisposer?: DragManager.DragDropDisposer;
@observable private hoverActive = false;
- @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
+ @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
+
+ @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
protected createDropTarget = (ele: HTMLDivElement) => {
if (this.dropDisposer) {
@@ -81,32 +82,18 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
console.log("IMPLEMENT ME PLEASE");
}
- @computed get extensionDoc() { return Doc.resolvedFieldDataDoc(this.dataDoc, this.props.fieldKey, "Alternates"); }
-
@undoBatch
@action
drop = (e: Event, de: DragManager.DropEvent) => {
if (de.data instanceof DragManager.DocumentDragData) {
- de.data.droppedDocuments.forEach(action((drop: Doc) => {
- if (de.mods === "AltKey" && /*this.dataDoc !== this.props.Document &&*/ drop.data instanceof ImageField) {
- Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new ImageField(drop.data.url);
- e.stopPropagation();
- } else if (de.mods === "MetaKey") {
- if (this.extensionDoc !== this.dataDoc) {
- let layout = StrCast(drop.backgroundLayout);
- if (layout.indexOf(ImageBox.name) !== -1) {
- let imgData = this.extensionDoc.Alternates;
- if (!imgData) {
- Doc.GetProto(this.extensionDoc).Alternates = new List([]);
- }
- let imgList = Cast(this.extensionDoc.Alternates, listSpec(Doc), [] as any[]);
- imgList && imgList.push(drop);
- e.stopPropagation();
- }
- }
- }
+ if (de.mods === "AltKey" && de.data.draggedDocuments.length && de.data.draggedDocuments[0].data instanceof ImageField) {
+ Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new ImageField(de.data.draggedDocuments[0].data.url);
+ e.stopPropagation();
+ }
+ de.mods === "MetaKey" && de.data.droppedDocuments.forEach(action((drop: Doc) => {
+ Doc.AddDocToList(Doc.GetProto(this.extensionDoc), "Alternates", drop);
+ e.stopPropagation();
}));
- // de.data.removeDocument() bcz: need to implement
}
}
@@ -243,9 +230,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
results.tags.map((tag: Tag) => {
tagsList.push(tag.name);
let sanitized = tag.name.replace(" ", "_");
- let script = `return (${tag.confidence} >= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`;
- let computed = CompileScript(script, { params: { this: "Doc" } });
- computed.compiled && (tagDoc[sanitized] = new ComputedField(computed));
+ tagDoc[sanitized] = ComputedField.MakeFunction(`(${tag.confidence} >= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`);
});
this.extensionDoc.generatedTags = tagsList;
tagDoc.title = "Generated Tags Doc";
@@ -261,13 +246,8 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
onDotDown(index: number) {
this.Document.curPage = index;
}
-
- @computed get fieldExtensionDoc() {
- return Doc.resolvedFieldDataDoc(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, "true");
- }
-
@computed private get url() {
- let data = Cast(Doc.GetProto(this.props.Document).data, ImageField);
+ let data = Cast(Doc.GetProto(this.props.Document)[this.props.fieldKey], ImageField);
return data ? data.url.href : undefined;
}
@@ -407,7 +387,6 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
// let [bptX, bptY] = transform.transformPoint(pw, this.props.PanelHeight());
// let w = bptX - sptX;
- let id = (this.props as any).id; // bcz: used to set id = "isExpander" in templates.tsx
let nativeWidth = FieldValue(this.Document.nativeWidth, pw);
let nativeHeight = FieldValue(this.Document.nativeHeight, 0);
let paths: string[] = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
@@ -433,13 +412,13 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
if (!this.props.Document.ignoreAspect && !this.props.leaveNativeSize) this.resize(srcpath, this.props.Document);
return (
- <div id={id} className={`imageBox-cont${interactive}`} style={{ background: "transparent" }}
+ <div className={`imageBox-cont${interactive}`} style={{ background: "transparent" }}
onPointerDown={this.onPointerDown}
onPointerEnter={action(() => this.hoverActive = true)}
onPointerLeave={action(() => this.hoverActive = false)}
onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
<div id="cf">
- <img id={id}
+ <img
key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
src={srcpath}
style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }}
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index f80f414b1..3a9318469 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -76,7 +76,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
} else if (type === "script") {
field = new ScriptField(script);
} else {
- let res = script.run({ this: target });
+ let res = script.run({ this: target }, console.log);
if (!res.success) return false;
field = res.result;
}
@@ -124,7 +124,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
let i = 0;
const self = this;
for (let key of Object.keys(ids).slice().sort()) {
- rows.push(<KeyValuePair doc={realDoc} ref={(function () {
+ rows.push(<KeyValuePair doc={realDoc} addDocTab={this.props.addDocTab} ref={(function () {
let oldEl: KeyValuePair | undefined;
return (el: KeyValuePair) => {
if (oldEl) self.rows.splice(self.rows.indexOf(oldEl), 1);
@@ -198,7 +198,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
return;
}
let previousViewType = fieldTemplate.viewType;
- Doc.MakeTemplate(fieldTemplate, metaKey, Doc.GetProto(parentStackingDoc));
+ Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(parentStackingDoc));
previousViewType && (fieldTemplate.viewType = previousViewType);
Cast(parentStackingDoc.data, listSpec(Doc))!.push(fieldTemplate);
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index a27dbd83d..1fed4c8bb 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,7 +1,7 @@
import { action, observable } from 'mobx';
import { observer } from "mobx-react";
import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
-import { Doc, Field } from '../../../new_fields/Doc';
+import { Doc, Field, Opt } from '../../../new_fields/Doc';
import { emptyFunction, returnFalse, returnOne, returnZero } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { Transform } from '../../util/Transform';
@@ -22,6 +22,7 @@ export interface KeyValuePairProps {
keyName: string;
doc: Doc;
keyWidth: number;
+ addDocTab: (doc: Doc, data: Opt<Doc>, where: string) => boolean;
}
@observer
export class KeyValuePair extends React.Component<KeyValuePairProps> {
@@ -45,7 +46,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
if (value instanceof Doc) {
e.stopPropagation();
e.preventDefault();
- ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.Create.KVPDocument(value, { width: 300, height: 300 }); CollectionDockingView.Instance.AddRightSplit(kvp, undefined); }, icon: "layer-group" });
+ ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" });
ContextMenu.Instance.displayMenu(e.clientX, e.clientY);
}
}
@@ -55,6 +56,8 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
Document: this.props.doc,
DataDoc: this.props.doc,
ContainingCollectionView: undefined,
+ ContainingCollectionDoc: undefined,
+ ruleProvider: undefined,
fieldKey: this.props.keyName,
fieldExt: "",
isSelected: returnFalse,
@@ -66,7 +69,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
focus: emptyFunction,
PanelWidth: returnZero,
PanelHeight: returnZero,
- addDocTab: returnZero,
+ addDocTab: returnFalse,
pinToPres: returnZero,
ContentScaling: returnOne
};
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index df35b603c..764051d62 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -4,40 +4,28 @@ import { observer } from "mobx-react";
import * as Pdfjs from "pdfjs-dist";
import "pdfjs-dist/web/pdf_viewer.css";
import 'react-image-lightbox/style.css';
-import { Doc, WidthSym, Opt } from "../../../new_fields/Doc";
+import { Doc, Opt, WidthSym } from "../../../new_fields/Doc";
import { makeInterface } from "../../../new_fields/Schema";
-import { ScriptField } from '../../../new_fields/ScriptField';
-import { BoolCast, Cast, NumCast } from "../../../new_fields/Types";
+import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
+import { Cast, NumCast } from "../../../new_fields/Types";
import { PdfField } from "../../../new_fields/URLField";
import { KeyCodes } from '../../northstar/utils/KeyCodes';
-import { CompileScript } from '../../util/Scripting';
+import { panZoomSchema } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { DocComponent } from "../DocComponent";
import { InkingControl } from "../InkingControl";
import { PDFViewer } from "../pdf/PDFViewer";
-import { positionSchema } from "./DocumentView";
+import { documentSchema } from "./DocumentView";
import { FieldView, FieldViewProps } from './FieldView';
import { pageSchema } from "./ImageBox";
import "./PDFBox.scss";
import React = require("react");
-type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
-const PdfDocument = makeInterface(positionSchema, pageSchema);
-export const handleBackspace = (e: React.KeyboardEvent) => { if (e.keyCode === KeyCodes.BACKSPACE) e.stopPropagation(); };
+type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
+const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
@observer
export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocument) {
public static LayoutString() { return FieldView.LayoutString(PDFBox); }
-
- @observable private _flyout: boolean = false;
- @observable private _alt = false;
- @observable private _pdf: Opt<Pdfjs.PDFDocumentProxy>;
-
- @computed get containingCollectionDocument() { return this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document; }
- @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
-
-
- @computed get fieldExtensionDoc() { return Doc.resolvedFieldDataDoc(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, "true"); }
-
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _reactionDisposer?: IReactionDisposer;
private _keyValue: string = "";
@@ -47,16 +35,26 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
private _valueRef: React.RefObject<HTMLInputElement> = React.createRef();
private _scriptRef: React.RefObject<HTMLInputElement> = React.createRef();
+ @observable private _flyout: boolean = false;
+ @observable private _alt = false;
+ @observable private _pdf: Opt<Pdfjs.PDFDocumentProxy>;
+
+ @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
+
+ @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
+
componentDidMount() {
this.props.setPdfBox && this.props.setPdfBox(this);
+ this.props.Document.curPage = ComputedField.MakeFunction("Math.floor(Number(this.panY) / Number(this.nativeHeight) + 1)");
+
const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
if (pdfUrl instanceof PdfField) {
Pdfjs.getDocument(pdfUrl.url.pathname).promise.then(pdf => runInAction(() => this._pdf = pdf));
}
this._reactionDisposer = reaction(
- () => this.props.Document.panY,
- () => this._mainCont.current && this._mainCont.current.scrollTo({ top: NumCast(this.Document.panY), behavior: "auto" })
+ () => this.Document.panY,
+ () => this._mainCont.current && this._mainCont.current.scrollTo({ top: this.Document.panY || 0, behavior: "auto" })
);
}
@@ -65,24 +63,22 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
}
public GetPage() {
- return Math.floor(NumCast(this.props.Document.panY) / NumCast(this.dataDoc.nativeHeight)) + 1;
+ return Math.floor((this.Document.panY || 0) / (this.Document.nativeHeight || 0)) + 1;
}
@action
public BackPage() {
- let cp = Math.ceil(NumCast(this.props.Document.panY) / NumCast(this.dataDoc.nativeHeight)) + 1;
+ let cp = Math.ceil((this.Document.panY || 0) / (this.Document.nativeHeight || 0)) + 1;
cp = cp - 1;
if (cp > 0) {
- this.props.Document.curPage = cp;
- this.props.Document.panY = (cp - 1) * NumCast(this.dataDoc.nativeHeight);
+ this.Document.panY = (cp - 1) * (this.Document.nativeHeight || 0);
}
}
@action
- public GotoPage(p: number) {
+ public GotoPage = (p: number) => {
if (p > 0 && p <= NumCast(this.dataDoc.numPages)) {
- this.props.Document.curPage = p;
- this.props.Document.panY = (p - 1) * NumCast(this.dataDoc.nativeHeight);
+ this.Document.panY = (p - 1) * (this.Document.nativeHeight || 0);
}
}
@@ -90,23 +86,20 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
public ForwardPage() {
let cp = this.GetPage() + 1;
if (cp <= NumCast(this.dataDoc.numPages)) {
- this.props.Document.curPage = cp;
- this.props.Document.panY = (cp - 1) * NumCast(this.dataDoc.nativeHeight);
+ this.Document.panY = (cp - 1) * (this.Document.nativeHeight || 0);
}
}
@action
setPanY = (y: number) => {
- this.containingCollectionDocument && (this.containingCollectionDocument.panY = y);
+ this.Document.panY = y;
}
@action
private applyFilter = () => {
- let scriptText = this._scriptValue.length > 0 ? this._scriptValue :
- this._keyValue.length > 0 && this._valueValue.length > 0 ?
- `return this.${this._keyValue} === ${this._valueValue}` : "return true";
- let script = CompileScript(scriptText, { params: { this: Doc.name } });
- script.compiled && (this.props.Document.filterScript = new ScriptField(script));
+ let scriptText = this._scriptValue ? this._scriptValue :
+ this._keyValue && this._valueValue ? `this.${this._keyValue} === ${this._valueValue}` : "true";
+ this.props.Document.filterScript = ScriptField.MakeFunction(scriptText);
}
scrollTo = (y: number) => {
@@ -114,8 +107,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
}
private resetFilters = () => {
- this._keyValue = this._valueValue = "";
- this._scriptValue = "return true";
+ this._keyValue = this._valueValue = this._scriptValue = "";
this._keyRef.current && (this._keyRef.current.value = "");
this._valueRef.current && (this._valueRef.current.value = "");
this._scriptRef.current && (this._scriptRef.current.value = "");
@@ -129,7 +121,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
return !this.props.active() ? (null) :
(<div className="pdfBox-settingsCont" onPointerDown={(e) => e.stopPropagation()}>
<button className="pdfBox-settingsButton" onClick={action(() => this._flyout = !this._flyout)} title="Open Annotation Settings"
- style={{ marginTop: `${this.containingCollectionDocument ? NumCast(this.containingCollectionDocument.panY) : 0}px` }}>
+ style={{ marginTop: `${this.Document.panY || 0}px` }}>
<div className="pdfBox-settingsButton-arrow"
style={{
borderTop: `25px solid ${this._flyout ? "#121721" : "transparent"}`,
@@ -146,13 +138,13 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
Annotation View Settings
</div>
<div className="pdfBox-settingsFlyout-kvpInput">
- <input placeholder="Key" className="pdfBox-settingsFlyout-input" onKeyDown={handleBackspace} onChange={this.newKeyChange}
+ <input placeholder="Key" className="pdfBox-settingsFlyout-input" onChange={this.newKeyChange}
style={{ gridColumn: 1 }} ref={this._keyRef} />
- <input placeholder="Value" className="pdfBox-settingsFlyout-input" onKeyDown={handleBackspace} onChange={this.newValueChange}
+ <input placeholder="Value" className="pdfBox-settingsFlyout-input" onChange={this.newValueChange}
style={{ gridColumn: 3 }} ref={this._valueRef} />
</div>
<div className="pdfBox-settingsFlyout-kvpInput">
- <input placeholder="Custom Script" onChange={this.newScriptChange} onKeyDown={handleBackspace} style={{ gridColumn: "1 / 4" }} ref={this._scriptRef} />
+ <input placeholder="Custom Script" onChange={this.newScriptChange} style={{ gridColumn: "1 / 4" }} ref={this._scriptRef} />
</div>
<div className="pdfBox-settingsFlyout-kvpInput">
<button style={{ gridColumn: 1 }} onClick={this.resetFilters}>
@@ -170,20 +162,20 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
loaded = (nw: number, nh: number, np: number) => {
this.dataDoc.numPages = np;
- if (!this.dataDoc.nativeWidth || !this.dataDoc.nativeHeight || !this.dataDoc.scrollHeight) {
- let oldaspect = NumCast(this.dataDoc.nativeHeight) / NumCast(this.dataDoc.nativeWidth, 1);
- this.dataDoc.nativeWidth = nw;
- this.dataDoc.nativeHeight = this.dataDoc.nativeHeight ? nw * oldaspect : nh;
- this.dataDoc.height = this.dataDoc[WidthSym]() * (nh / nw);
- this.dataDoc.scrollHeight = np * this.dataDoc.nativeHeight;
+ if (!this.Document.nativeWidth || !this.Document.nativeHeight || !this.Document.scrollHeight) {
+ let oldaspect = (this.Document.nativeHeight || 0) / (this.Document.nativeWidth || 1);
+ this.Document.nativeWidth = nw;
+ this.Document.nativeHeight = this.Document.nativeHeight ? nw * oldaspect : nh;
+ this.Document.height = this.Document[WidthSym]() * (nh / nw);
+ this.Document.scrollHeight = np * this.Document.nativeHeight;
}
}
@action
onScroll = (e: React.UIEvent<HTMLDivElement>) => {
- if (e.currentTarget && this.containingCollectionDocument) {
- this.containingCollectionDocument.panTransformType = "None";
- this.containingCollectionDocument.panY = e.currentTarget.scrollTop;
+ if (e.currentTarget && this.props.ContainingCollectionDoc) {
+ this.props.Document.panTransformType = "None";
+ this.Document.panY = e.currentTarget.scrollTop;
}
}
@@ -195,14 +187,14 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
<div>{`pdf, ${this.dataDoc[this.props.fieldKey]}, not found`}</div> :
<div className={classname}
onScroll={this.onScroll}
- style={{ marginTop: `${this.containingCollectionDocument ? NumCast(this.containingCollectionDocument.panY) : 0}px` }}
+ style={{ marginTop: `${(this.Document.panY || 0)}px` }}
ref={this._mainCont}>
- <div className="pdfBox-scrollHack" style={{ height: NumCast(this.props.Document.scrollHeight) + (NumCast(this.props.Document.nativeHeight) - NumCast(this.props.Document.nativeHeight) / NumCast(this.props.Document.scale, 1)), width: "100%" }} />
- <PDFViewer pdf={this._pdf} url={pdfUrl.url.pathname} active={this.props.active} scrollTo={this.scrollTo} loaded={this.loaded} panY={NumCast(this.props.Document.panY)}
- Document={this.props.Document} DataDoc={this.props.DataDoc}
- addDocTab={this.props.addDocTab} setPanY={this.setPanY}
+ <div className="pdfBox-scrollHack" style={{ height: NumCast(this.props.Document.scrollHeight) + ((this.Document.nativeHeight || 0) - (this.Document.nativeHeight || 0) / (this.Document.scale || 1)) }} />
+ <PDFViewer pdf={this._pdf} url={pdfUrl.url.pathname} active={this.props.active} scrollTo={this.scrollTo} loaded={this.loaded} panY={this.Document.panY || 0}
+ Document={this.props.Document} DataDoc={this.dataDoc}
+ addDocTab={this.props.addDocTab} setPanY={this.setPanY} GoToPage={this.GotoPage}
pinToPres={this.props.pinToPres} addDocument={this.props.addDocument}
- fieldKey={this.props.fieldKey} fieldExtensionDoc={this.fieldExtensionDoc} />
+ fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} />
{this.settingsPanel()}
</div>);
}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 3f4ee8960..b7d9a1eab 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -4,7 +4,7 @@ import { observer } from "mobx-react";
import * as rp from 'request-promise';
import { InkTool } from "../../../new_fields/InkField";
import { makeInterface } from "../../../new_fields/Schema";
-import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
+import { Cast, FieldValue, NumCast, BoolCast } from "../../../new_fields/Types";
import { VideoField } from "../../../new_fields/URLField";
import { RouteStore } from "../../../server/RouteStore";
import { Utils } from "../../../Utils";
@@ -14,19 +14,18 @@ import { ContextMenuProps } from "../ContextMenuItem";
import { DocComponent } from "../DocComponent";
import { DocumentDecorations } from "../DocumentDecorations";
import { InkingControl } from "../InkingControl";
-import { positionSchema } from "./DocumentView";
+import { documentSchema } from "./DocumentView";
import { FieldView, FieldViewProps } from './FieldView';
import { pageSchema } from "./ImageBox";
import "./VideoBox.scss";
import { library } from "@fortawesome/fontawesome-svg-core";
import { faVideo } from "@fortawesome/free-solid-svg-icons";
-import { CompileScript } from "../../util/Scripting";
import { Doc } from "../../../new_fields/Doc";
import { ScriptField } from "../../../new_fields/ScriptField";
var path = require('path');
-type VideoDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
-const VideoDocument = makeInterface(positionSchema, pageSchema);
+type VideoDocument = makeInterface<[typeof documentSchema, typeof pageSchema]>;
+const VideoDocument = makeInterface(documentSchema, pageSchema);
library.add(faVideo);
@@ -116,18 +115,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
x: NumCast(this.props.Document.x) + width, y: NumCast(this.props.Document.y),
width: 150, height: 50, title: NumCast(this.props.Document.curPage).toString()
});
- const script = CompileScript(`(self as any).curPage = ${NumCast(this.props.Document.curPage)}`, {
- params: { this: Doc.name },
- capturedVariables: { self: this.props.Document },
- typecheck: false,
- editable: true,
- });
- if (script.compiled) {
- b.onClick = new ScriptField(script);
- this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.addDocument && this.props.ContainingCollectionView.props.addDocument(b, false);
- } else {
- console.log(script.errors.map(error => error.messageText).join("\n"));
- }
+ b.onClick = ScriptField.MakeScript(`this.curPage = ${NumCast(this.props.Document.curPage)}`);
} else {
//convert to desired file format
var dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
@@ -204,7 +192,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
}
}
specificContextMenu = (e: React.MouseEvent): void => {
- let field = Cast(this.Document[this.props.fieldKey], VideoField);
+ let field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
if (field) {
let url = field.url.href;
let subitems: ContextMenuProps[] = [];
@@ -216,7 +204,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
}
@computed get content() {
- let field = Cast(this.Document[this.props.fieldKey], VideoField);
+ let field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
let style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
return !field ? <div>Loading</div> :
@@ -228,7 +216,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
}
@computed get youtubeVideoId() {
- let field = Cast(this.Document[this.props.fieldKey], VideoField);
+ let field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
return field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
}
@@ -269,6 +257,8 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
}
+ @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
+
@computed get youtubeContent() {
this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
@@ -281,6 +271,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
}
render() {
+ Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
return <div style={{ pointerEvents: "all", width: "100%", height: "100%" }} onContextMenu={this.specificContextMenu}>
{this.youtubeVideoId ? this.youtubeContent : this.content}
</div>;