aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTyler Schicke <tyler_schicke@brown.edu>2019-07-02 19:02:51 -0400
committerTyler Schicke <tyler_schicke@brown.edu>2019-07-02 19:02:51 -0400
commita8664face6f19cd2373bd928c2a2b3043bb6278d (patch)
tree9a381574784ec6df3cfc0a972f2ef9b743ee53ed
parente68a306f8b75d84b589fff19ed8a141fe2162355 (diff)
Added arrange script to freeform views
-rw-r--r--src/client/views/MainView.tsx4
-rw-r--r--src/client/views/OverlayView.tsx46
-rw-r--r--src/client/views/ScriptBox.tsx36
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx41
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx12
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx12
6 files changed, 141 insertions, 10 deletions
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 04ecc47cf..cc39dc04c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -38,6 +38,7 @@ import PDFMenu from './pdf/PDFMenu';
import { InkTool } from '../../new_fields/InkField';
import _ from "lodash";
import KeyManager from './GlobalKeyHandler';
+import { OverlayView } from './OverlayView';
@observer
export class MainView extends React.Component {
@@ -291,7 +292,7 @@ export class MainView extends React.Component {
} else {
CollectionDockingView.Instance.AddRightSplit(doc, dataDoc);
}
- }
+ };
let flyout = <DocumentView
Document={CurrentUserUtils.UserDocument}
DataDoc={undefined}
@@ -441,6 +442,7 @@ export class MainView extends React.Component {
{this.miscButtons}
<PDFMenu />
<MainOverlayTextBox />
+ <OverlayView />
</div>
);
}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
new file mode 100644
index 000000000..72f1068ce
--- /dev/null
+++ b/src/client/views/OverlayView.tsx
@@ -0,0 +1,46 @@
+import * as React from "react";
+import { observer } from "mobx-react";
+import { observable, action } from "mobx";
+
+export type OverlayDisposer = () => void;
+
+export type OverlayElementOptions = {
+ x: number;
+ y: number;
+ width?: number;
+ height?: number;
+};
+
+@observer
+export class OverlayView extends React.Component {
+ public static Instance: OverlayView;
+ @observable.shallow
+ private _elements: { ele: JSX.Element, options: OverlayElementOptions }[] = [];
+
+ constructor(props: any) {
+ super(props);
+ if (!OverlayView.Instance) {
+ OverlayView.Instance = this;
+ }
+ }
+
+ @action
+ addElement(ele: JSX.Element, options: OverlayElementOptions): OverlayDisposer {
+ const eleWithPosition = { ele, options };
+ this._elements.push(eleWithPosition);
+ return action(() => {
+ const index = this._elements.indexOf(eleWithPosition);
+ if (index !== -1) this._elements.splice(index, 1);
+ });
+ }
+
+ render() {
+ return (
+ <div>
+ {this._elements.map(({ ele, options: { x, y, width, height } }) => (
+ <div style={{ position: "absolute", transform: `translate(${x}px, ${y}px)`, width, height }}>{ele}</div>
+ ))}
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
new file mode 100644
index 000000000..aea9d52a4
--- /dev/null
+++ b/src/client/views/ScriptBox.tsx
@@ -0,0 +1,36 @@
+import * as React from "react";
+import { observer } from "mobx-react";
+import { observable, action } from "mobx";
+
+export interface ScriptBoxProps {
+ onSave: (text: string, onError: (error: string) => void) => void;
+ onCancel?: () => void;
+}
+
+@observer
+export class ScriptBox extends React.Component<ScriptBoxProps> {
+ @observable
+ private _scriptText: string = "";
+
+ @action
+ onChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
+ this._scriptText = e.target.value;
+ }
+
+ @action
+ onError = (error: string) => {
+ console.log(error);
+ }
+
+ render() {
+ return (
+ <div className="scriptBox-outerDiv">
+ <div className="scriptBox-toolbar">
+ <button onClick={e => { this.props.onSave(this._scriptText, this.onError); e.stopPropagation(); }}>Save</button>
+ <button onClick={e => { this.props.onCancel && this.props.onCancel(); e.stopPropagation(); }}>Cancel</button>
+ </div>
+ <textarea className="scriptBox-textarea" onChange={this.onChange} value={this._scriptText}></textarea>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 169dbe540..23c0fdd0d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -27,6 +27,10 @@ import "./CollectionFreeFormView.scss";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
import v5 = require("uuid/v5");
+import { ScriptField } from "../../../../new_fields/ScriptField";
+import { OverlayView } from "../../OverlayView";
+import { ScriptBox } from "../../ScriptBox";
+import { CompileScript } from "../../../util/Scripting";
export const panZoomSchema = createSchema({
@@ -388,6 +392,18 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
};
}
+ getCalculatedPositions(doc: Doc, index: number, collection: Doc): { x?: number, y?: number, width?: number, height?: number } | undefined {
+ const script = Cast(this.props.Document.arrangeScript, ScriptField);
+ if (!script) {
+ return undefined;
+ }
+ const result = script.script.run({ doc, index, collection });
+ if (!result.success) {
+ return undefined;
+ }
+ return result.result;
+ }
+
@computed.struct
get views() {
let curPage = FieldValue(this.Document.curPage, -1);
@@ -397,7 +413,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
if (Math.round(page) === Math.round(curPage) || page === -1) {
let minim = BoolCast(doc.isMinimized, false);
if (minim === undefined || !minim) {
- prev.push(<CollectionFreeFormDocumentView key={doc[Id]} {...this.getChildDocumentViewProps(doc)} />);
+ const pos = this.getCalculatedPositions(doc, prev.length, this.Document) || {};
+ prev.push(<CollectionFreeFormDocumentView key={doc[Id]} x={pos.x} y={pos.y} width={pos.width} height={pos.height} {...this.getChildDocumentViewProps(doc)} />);
}
}
return prev;
@@ -438,6 +455,28 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
}
});
+ ContextMenu.Instance.addItem({
+ description: "Add freeform arrangement",
+ event: () => {
+ let overlayDisposer: () => void;
+ let scriptingBox = <ScriptBox onCancel={() => overlayDisposer()} onSave={(text, onError) => {
+ const script = CompileScript(text, {
+ params: {
+ doc: "Doc", index: "number", collection: "Doc"
+ },
+ requiredType: "{x: number, y: number, width?: number, height?: number}",
+ typecheck: false
+ });
+ if (!script.compiled) {
+ onError(script.errors.map(error => error.messageText).join("\n"));
+ return;
+ }
+ this.props.Document.arrangeScript = new ScriptField(script);
+ overlayDisposer();
+ }} />;
+ overlayDisposer = OverlayView.Instance.addElement(scriptingBox, { x: 100, y: 100, width: 200, height: 200 });
+ }
+ });
}
private childViews = () => [
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 858959d81..8556817cc 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -9,6 +9,10 @@ import "./DocumentView.scss";
import React = require("react");
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
+ x?: number;
+ y?: number;
+ width?: number;
+ height?: number;
}
const schema = createSchema({
@@ -23,13 +27,13 @@ const FreeformDocument = makeInterface(schema, 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) scale(${this.zoom}) `; }
- @computed get X() { return FieldValue(this.Document.x, 0); }
- @computed get Y() { return FieldValue(this.Document.y, 0); }
+ @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 width(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : FieldValue(this.Document.width, 0); }
- @computed get height(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : FieldValue(this.Document.height, 0); }
set width(w: number) {
this.Document.width = w;
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 0e798d291..9407d742c 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -9,7 +9,7 @@ import { KeyValuePair } from "./KeyValuePair";
import React = require("react");
import { NumCast, Cast, FieldValue, StrCast } from "../../../new_fields/Types";
import { Doc, Field, FieldResult } from "../../../new_fields/Doc";
-import { ComputedField } from "../../../new_fields/ScriptField";
+import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
import { SetupDrag } from "../../util/DragManager";
import { Docs } from "../../documents/Documents";
import { RawDataOperationParameters } from "../../northstar/model/idea/idea";
@@ -50,7 +50,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
let eq = value.startsWith("=");
let target = eq ? doc : Doc.GetProto(doc);
value = eq ? value.substr(1) : value;
- let dubEq = value.startsWith(":=");
+ let dubEq = value.startsWith(":=") ? 1 : value.startsWith(";=") ? 2 : 0;
value = dubEq ? value.substr(2) : value;
let options: ScriptOptions = { addReturn: true, params: { this: "Doc" } };
if (dubEq) options.typecheck = false;
@@ -58,8 +58,12 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
if (!script.compiled) {
return false;
}
- let field = new ComputedField(script);
- if (!dubEq) {
+ let field: Field;
+ if (dubEq === 1) {
+ field = new ComputedField(script);
+ } else if (dubEq === 2) {
+ field = new ScriptField(script);
+ } else {
let res = script.run({ this: target });
if (!res.success) return false;
field = res.result;