aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
diff options
context:
space:
mode:
authorkimdahey <claire_kim1@brown.edu>2019-09-17 16:16:20 -0400
committerkimdahey <claire_kim1@brown.edu>2019-09-17 16:16:20 -0400
commitf520d5a821b515e0c216f055e93f0549bd6733a9 (patch)
treed55d25a4280bf89d3c39fd8989700ca00b328a2e /src/client/views/nodes/CollectionFreeFormDocumentView.tsx
parent147c208a9a1196d2ded540e28dc3d7f26d5ba0a9 (diff)
parent1310633790e3db50a31a1cc6d357306d7884a053 (diff)
fixed merge conflicts
Diffstat (limited to 'src/client/views/nodes/CollectionFreeFormDocumentView.tsx')
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx158
1 files changed, 86 insertions, 72 deletions
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 9692dd8a9..19d4a6784 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 } 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 {
@@ -17,56 +18,80 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
height?: number;
jitterRotation: number;
}
-
-const schema = createSchema({
- zoomBasis: "number",
+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) {
- @computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${random(-1, 1) * this.props.jitterRotation}deg) scale(${this.zoom}) `; }
- @computed get X() { return this.props.x !== undefined ? this.props.x : this.Document.x || 0; }
- @computed get Y() { return this.props.y !== undefined ? this.props.y : this.Document.y || 0; }
- @computed get width(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.props.width !== undefined ? this.props.width : this.Document.width || 0; }
- @computed get height(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.props.height !== undefined ? this.props.height : this.Document.height || 0; }
- @computed get zoom(): number { return 1 / FieldValue(this.Document.zoomBasis, 1); }
- @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); }
+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._animx !== undefined ? this._animx : this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.Document.x || 0; }
+ @computed get Y() { return this._animy !== undefined ? this._animy : 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.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;
+ let w = NumCast(someView.width) / NumCast(someView.scale) / NumCast(minimap.fitW) * NumCast(minimap.width);
+ let h = NumCast(someView.height) / NumCast(someView.scale) / NumCast(minimap.fitH) * NumCast(minimap.height);
+ return { x: x, y: y, width: w, height: h };
+ }
+ }
+ return undefined;
+ }
+
+ componentWillUnmount() {
+ this._disposer && this._disposer();
+ }
+ componentDidMount() {
+ this._disposer = reaction(() => this.props.Document.iconTarget,
+ () => {
+ const icon = this.props.Document.iconTarget ? Array.from(Cast(this.props.Document.iconTarget, listSpec("number"))!) : undefined;
+ if (icon) {
+ let target = this.props.ScreenToLocalTransform().transformPoint(icon[0], icon[1]);
+ if (icon[2] === 1) {
+ this._animx = target[0];
+ this._animy = target[1];
+ }
+ setTimeout(action(() => {
+ this._animx = icon[2] === 1 ? this.Document.x : target[0];
+ this._animy = icon[2] === 1 ? this.Document.y : target[1];
+ }), 25);
+ } else {
+ this._animx = this._animy = undefined;
+ }
+ }, { fireImmediately: true });
+ }
- contentScaling = () => this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? this.width / this.nativeWidth : 1;
+ 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.zoom / 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];
- });
- }
+ .scale(1 / this.contentScaling()).scale(1 / this.scaleToOverridingWidth);
borderRounding = () => {
- let br = StrCast(this.props.Document.layout instanceof Doc ? this.props.Document.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.props.Document.nativeWidth), NumCast(this.props.Document.nativeHeight));
- let minDim = percent * (nativeDim ? nativeDim : Math.min(this.props.PanelWidth(), this.props.PanelHeight()));
- return minDim;
+ let nativeDim = Math.min(NumCast(this.layoutDoc.nativeWidth), NumCast(this.layoutDoc.nativeHeight));
+ return percent2frac(br) * (nativeDim ? nativeDim : Math.min(this.props.PanelWidth(), this.props.PanelHeight()));
}
return undefined;
}
@@ -76,41 +101,31 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
clusterColorFunc = (doc: Doc) => this.clusterColor;
+ 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;
+ }
+
+ @observable _animx: number | undefined = undefined;
+ @observable _animy: number | undefined = undefined;
+
render() {
- let txf = this.transform;
- let w = this.width;
- let h = this.height;
- let renderScript = this.Document.renderScript;
- if (renderScript) {
- let someView = Cast(this.Document.someView, Doc);
- let minimap = Cast(this.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;
- w = NumCast(someView.width) / NumCast(someView.scale) / NumCast(minimap.fitW) * NumCast(minimap.width);
- h = NumCast(someView.height) / NumCast(someView.scale) / NumCast(minimap.fitH) * NumCast(minimap.height);
- txf = `translate(${x}px,${y}px)`;
- }
- }
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.props.Document.opacity === 0 ? undefined : // if it's not visible, then no shadow
- this.props.Document.z ? `#9c9396 ${StrCast(this.props.Document.boxShadow, "10px 10px 0.9vw")}` : // if it's a floating doc, give it a big shadow
- this.clusterColor ? (
- this.props.Document.isBackground ? `0px 0px 50px 50px ${this.clusterColor}` : // if it's a background & has a cluster color, make the shadow spread really big
- `${this.clusterColor} ${StrCast(this.props.Document.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
- undefined,
+ 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.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: txf,
- transition: hasPosition ? "transform 1s" : StrCast(this.props.Document.transition),
- width: w,
- height: h,
+ transform: this.transform,
+ transition: this.props.Document.isIconAnimating ? "transform .5s" : hasPosition ? "transform 1s" : StrCast(this.layoutDoc.transition),
+ width: this.width,
+ height: this.height,
zIndex: this.Document.zIndex || 0,
}} >
<DocumentView {...this.props}
@@ -119,7 +134,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
backgroundColor={this.clusterColorFunc}
PanelWidth={this.panelWidth}
PanelHeight={this.panelHeight}
- animateBetweenIcon={this.animateBetweenIcon}
/>
</div>
);