From 94db1b622918d473500813e0aa28f91fdba6145c Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 26 May 2020 02:09:50 -0400 Subject: restructured ScriptingBox code. --- src/client/views/nodes/ScriptingBox.tsx | 762 ++++++++++---------------------- 1 file changed, 230 insertions(+), 532 deletions(-) (limited to 'src/client/views/nodes/ScriptingBox.tsx') diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index 5bcb5f348..db8a8b559 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -1,30 +1,28 @@ -import { action, observable, computed, _allowStateChangesInsideComputed, runInAction } from "mobx"; +import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import * as React from "react"; +import { Doc } from "../../../fields/Doc"; import { documentSchema } from "../../../fields/documentSchemas"; -import { createSchema, makeInterface, listSpec } from "../../../fields/Schema"; -import { ScriptField, ComputedField } from "../../../fields/ScriptField"; -import { StrCast, ScriptCast, Cast, NumCast } from "../../../fields/Types"; +import { List } from "../../../fields/List"; +import { createSchema, listSpec, makeInterface } from "../../../fields/Schema"; +import { ScriptField } from "../../../fields/ScriptField"; +import { Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; +import { returnEmptyString } from "../../../Utils"; +import { DragManager } from "../../util/DragManager"; import { InteractionUtils } from "../../util/InteractionUtils"; -import { CompileScript, ScriptSucccess, ScriptParam } from "../../util/Scripting"; +import { CompileScript, ScriptParam } from "../../util/Scripting"; +import { ContextMenu } from "../ContextMenu"; import { ViewBoxAnnotatableComponent } from "../DocComponent"; import { EditableView } from "../EditableView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; -import "./ScriptingBox.scss"; import { OverlayView } from "../OverlayView"; -import { DocumentIconContainer, DocumentIcon } from "./DocumentIcon"; -import { List } from "../../../fields/List"; -import { DragManager } from "../../util/DragManager"; -import { ContextMenuProps } from "../ContextMenuItem"; -import { copy } from "typescript-collections/dist/lib/arrays"; -import { Doc, WidthSym } from "../../../fields/Doc"; -import { ContextMenu } from "../ContextMenu"; +import { DocumentIconContainer } from "./DocumentIcon"; +import "./ScriptingBox.scss"; const ScriptingSchema = createSchema({}); type ScriptingDocument = makeInterface<[typeof ScriptingSchema, typeof documentSchema]>; const ScriptingDocument = makeInterface(ScriptingSchema, documentSchema); - @observer export class ScriptingBox extends ViewBoxAnnotatableComponent(ScriptingDocument) { @@ -32,166 +30,72 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent void; - rowProps: any; - _paramNum: number = 0; @observable private _errorMessage: string = ""; @observable private _applied: boolean = false; - // @observable private _paramsNames: string[] = []; - // @observable private _paramsTypes: string[] = []; - // @observable private _paramsValues: string[] = []; - // @observable private _paramsCollapsed: boolean[] = []; - - protected createDashEventsTarget = (ele: HTMLDivElement, dropFunc: (e: Event, de: DragManager.DropEvent) => void) => { //used for stacking and masonry view - if (ele) { - this.dropDisposer?.(); - this.dropDisposer = DragManager.MakeDropTarget(ele, dropFunc, this.layoutDoc); - } - } + @computed get paramsNames() { return this.compileParams.map(p => p.split(":")[0].trim()); } + @computed get paramsTypes() { return this.compileParams.map(p => p.split(":")[1].trim()); } @computed get rawScript() { return StrCast(this.dataDoc[this.props.fieldKey + "-rawScript"], ""); } @computed get compileParams() { return Cast(this.dataDoc[this.props.fieldKey + "-params"], listSpec("string"), []); } set rawScript(value) { this.dataDoc[this.props.fieldKey + "-rawScript"] = value; } - - @computed get _paramsNames() { return Cast(this.dataDoc[this.props.fieldKey + "-paramsNames"], listSpec("string"), null); } - @computed get _paramsTypes() { return Cast(this.dataDoc[this.props.fieldKey + "-paramsTypes"], listSpec("string"), null); } - @computed get _paramsValues() { return Cast(this.dataDoc[this.props.fieldKey + "-paramsValues"], listSpec("string"), null); } - @computed get _paramsCollapsed() { return Cast(this.dataDoc[this.props.fieldKey + "-paramsCollapsed"], listSpec("boolean"), null); } - - set compileParams(value) { this.dataDoc[this.props.fieldKey + "-params"] = value; } - - set _paramsNames(value: string[]) { this.dataDoc[this.props.fieldKey + "-paramsNames"] = new List(value); } - set _paramsTypes(value: string[]) { this.dataDoc[this.props.fieldKey + "-paramsTypes"] = new List(value); } - set _paramsValues(value: string[]) { this.dataDoc[this.props.fieldKey + "-paramsValues"] = new List(value); } - set _paramsCollapsed(value: boolean[]) { this.dataDoc[this.props.fieldKey + "-paramsCollapsed"] = new List(value); } - - stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation(); + set compileParams(value) { this.dataDoc[this.props.fieldKey + "-params"] = new List(value); } @action componentDidMount() { - this.rawScript = ScriptCast(this.dataDoc[this.props.fieldKey])?.script?.originalScript || this.rawScript; + this.rawScript = ScriptCast(this.dataDoc[this.props.fieldKey])?.script?.originalScript ?? this.rawScript; } componentWillUnmount() { this._overlayDisposer?.(); } + protected createDashEventsTarget = (ele: HTMLDivElement, dropFunc: (e: Event, de: DragManager.DropEvent) => void) => { //used for stacking and masonry view + if (ele) { + this.dropDisposer?.(); + this.dropDisposer = DragManager.MakeDropTarget(ele, dropFunc, this.layoutDoc); + } + } + @action onFinish = () => { - const result = CompileScript(this.rawScript, {}); this.rootDoc.layoutKey = "layout"; - this.rootDoc.height = 50; - this.rootDoc.width = 100; + this.rootDoc._height = 50; + this.rootDoc._width = 100; this.dataDoc.documentText = this.rawScript; } @action onError = (error: any) => { - for (const entry of error) { - this._errorMessage = this._errorMessage + " " + entry.messageText; - } + this._errorMessage = error?.map((entry: any) => entry.messageText).join(" ") || ""; } @action onCompile = () => { - // const params = this.compileParams.reduce((o: ScriptParam, p: string) => { o[p] = "any"; return o; }, {} as ScriptParam); - // const result = CompileScript(this.rawScript, { - // editable: true - // transformer: DocumentIconContainer.getTransformer(), - // params, - // typecheck: false - // }); - // this._errorMessage = isCompileError(result) ? result.errors.map(e => e.messageText).join("\n") : ""; - // return this.dataDoc[this.props.fieldKey] = result.compiled ? new ScriptField(result) : undefined; - - const params = this.compileParams.reduce((o: ScriptParam, p: string) => { - const param = p.split(":"); - - o[param[0].trim()] = param[1].trim(); - return o; - }, - {} as ScriptParam); - - console.log(this.compileParams); + const params: ScriptParam = {}; + this.compileParams.forEach(p => params[p.split(":")[0].trim()] = p.split(":")[1].trim()); const result = CompileScript(this.rawScript, { editable: true, transformer: DocumentIconContainer.getTransformer(), params, - typecheck: false + typecheck: true }); - this._errorMessage = ""; - if (result.compiled) { - this._errorMessage = ""; - this.dataDoc.data = new ScriptField(result); - } - else { - this.onError(result.errors); - } this.dataDoc.documentText = this.rawScript; + this.dataDoc.data = result.compiled ? new ScriptField(result) : undefined; + this.onError(result.compiled ? undefined : result.errors); } - @action onRun = () => { - const params = this.compileParams.reduce((o: ScriptParam, p: string) => { - const param = p.split(":"); - o[param[0].trim()] = param[1].trim(); - return o; - }, - {} as ScriptParam); - + this.onCompile(); const bindings: { [name: string]: any } = {}; - Array.from(Object.keys(params)).forEach(key => bindings[key] = this.dataDoc[key]); - const result = CompileScript(this.rawScript, { - editable: true, - transformer: DocumentIconContainer.getTransformer(), - params, - typecheck: true - }); - this._errorMessage = ""; - if (result.compiled) { - // this automatically saves - result.run({ self: this.rootDoc, this: this.layoutDoc, ...bindings }, (err: any) => { - this._errorMessage = ""; - this.onError(err); - }); - this.dataDoc.data = new ScriptField(result); - } - else { - this.onError(result.errors); - } - this.dataDoc.documentText = this.rawScript; - //this.onCompile()?.script.run({}, err => this._errorMessage = err.map((e: any) => e.messageText).join("\n")); + this.paramsNames.forEach(key => bindings[key] = this.dataDoc[key]); + ScriptCast(this.dataDoc.data, null)?.script.run({ self: this.rootDoc, this: this.layoutDoc, ...bindings }, this.onError); } @action onApply = () => { - const params = this.compileParams.reduce((o: ScriptParam, p: string) => { - const param = p.split(":"); - - o[param[0].trim()] = param[1].trim(); - return o; - }, - {} as ScriptParam); - - console.log(this.compileParams); - - const result = CompileScript(this.rawScript, { - editable: true, - transformer: DocumentIconContainer.getTransformer(), - params, - typecheck: false - }); - this._errorMessage = ""; - if (result.compiled) { - this._errorMessage = ""; - this.dataDoc.data = new ScriptField(result); - - this._applied = true; - } - else { - this.onError(result.errors); - } - this.dataDoc.documentText = this.rawScript; + this.onCompile(); + this._applied = true; } @action @@ -206,453 +110,247 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent { - console.log("drop"); - const droppedDocs = de.complete.docDragData?.droppedDocuments; - if (droppedDocs?.length) { - const dropped = droppedDocs[0]; - this.dataDoc[fieldKey] = dropped; - const num = this._paramsNames.indexOf(fieldKey); - this._paramsValues[num] = StrCast(dropped.title); - } + this.dataDoc[fieldKey] = de.complete.docDragData?.droppedDocuments[0]; e.stopPropagation(); } @action onDelete = (num: number) => { + this.dataDoc[this.paramsNames[num]] = undefined; this.compileParams.splice(num, 1); - const name = this._paramsNames[this._paramsNames.length - num - 1]; - this.dataDoc[name] = undefined; - this._paramsNames.splice(this._paramsNames.length - num - 1, 1); - this._paramsTypes.splice(this._paramsTypes.length - num - 1, 1); - this._paramsValues.splice(this._paramsValues.length - num - 1, 1); + return true; } @action - viewChanged = (e: React.ChangeEvent, i: number, name: string) => { + viewChanged = (e: React.ChangeEvent, name: string) => { //@ts-ignore this.dataDoc[name] = e.target.selectedOptions[0].value; - //@ts-ignore - this._paramsValues[i] = e.target.selectedOptions[0].value; - } - - @action - selected = (val: string, index: number, name: string) => { - this.stopPropagation; - this.dataDoc[name] = val; - this._paramsValues[index] = val; - } - - @action - toggleCollapse = (num: number) => { - if (this._paramsCollapsed[num]) { - this._paramsCollapsed[num] = false; - } else { - this._paramsCollapsed[num] = true; - } - } - - @action - selectedBool = (val: boolean, index: number, name: string) => { - this.stopPropagation; - if (val) { - this._paramsValues[index] = "true"; - this.dataDoc[name] = true; - } else { - this._paramsValues[index] = "false"; - this.dataDoc[name] = false; - } } onCopy = () => { const copy = Doc.MakeCopy(this.rootDoc, true); - copy.x = NumCast(this.dataDoc._width) + NumCast(this.dataDoc.x); + copy.x = NumCast(this.dataDoc.x) + NumCast(this.dataDoc._width); this.props.addDocument?.(copy); } specificContextMenu = (e: React.MouseEvent): void => { - const existingMore = ContextMenu.Instance.findByDescription("More..."); - const mores: ContextMenuProps[] = existingMore && "subitems" in existingMore ? existingMore.subitems : []; - mores.push({ description: "Create a Copy", event: this.onCopy, icon: "copy" }); - !existingMore && ContextMenu.Instance.addItem({ description: "More...", subitems: mores, icon: "hand-point-right" }); + const existingOptions = ContextMenu.Instance.findByDescription("Options..."); + const options = existingOptions && "subitems" in existingOptions ? existingOptions.subitems : []; + options.push({ description: "Create a Copy", event: this.onCopy, icon: "copy" }); + !existingOptions && ContextMenu.Instance.addItem({ description: "Options...", subitems: options, icon: "hand-point-right" }); } + renderErrorMessage() { + return !this._errorMessage ? (null) :
{this._errorMessage}
; + } - - render() { - - const params = ""} - SetValue={value => { - if (value !== "" && value !== " ") { - const parameter = value.split(":"); - if (parameter[1] !== undefined) { - if (parameter[1].trim() === "Doc" || parameter[1].trim() === "string" - || parameter[1].split("|")[1] || parameter[1].trim() === "number" - || parameter[1].trim() === "boolean") { - - if (!this._paramsNames?.includes(parameter[0].trim()) && - this.dataDoc[parameter[0].trim()] === undefined) { - - this._errorMessage = ""; - this._paramNum++; - const par = this.compileParams; - this.compileParams = new List(value.split(";").filter(s => s !== " ")); - this.compileParams.push.apply(this.compileParams, par); - if (this._paramsNames === undefined) this._paramsNames = []; - if (this._paramsTypes === undefined) this._paramsTypes = []; - if (this._paramsValues === undefined) this._paramsValues = []; - if (this._paramsCollapsed === undefined) this._paramsCollapsed = []; - this._paramsNames.push(parameter[0].trim()); - this._paramsTypes.push(parameter[1].trim()); - this._paramsValues.push("undefined"); - this._paramsCollapsed.push(true); - return true; - - } else { - this._errorMessage = "this name has already been used"; - return false; - } - } else { - this._errorMessage = "this type is not supported"; - return false; - } - } else { - this._errorMessage = "must set type of parameter"; - return false; + renderDoc(parameter: string) { + return
this._overlayDisposer?.()} + ref={ele => ele && this.createDashEventsTarget(ele, (e, de) => this.onDrop(e, de, parameter))} > + this.dataDoc[parameter]?.title ?? ""} + SetValue={action((value: string) => { + const script = CompileScript(value, { + addReturn: true, + typecheck: false, + transformer: DocumentIconContainer.getTransformer() + }); + const results = script.compiled && script.run(); + if (results && results.success) { + this._errorMessage = ""; + this.dataDoc[parameter] = results.result; + return true; } - } - return false; - }} - />; + this._errorMessage = "invalid document"; + return false; + })} + /> +
; + } + renderString(parameter: string) { + return
+ StrCast(this.dataDoc[parameter]) ?? "undefined"} + SetValue={action((value: string) => { + if (value && value !== " ") { + this._errorMessage = ""; + this.dataDoc[parameter] = value; + return true; + } + return false; + })} + /> +
; + } - // might want to change this display later on - const listParams = this.compileParams.map((parameter, i) => -
e.key === "Enter" && this._overlayDisposer?.()} - > - parameter} - SetValue={value => { - if (value && value !== " ") { - const parameter = value.split(":"); - if (parameter[1] !== undefined) { - if (parameter[1].trim() === "Doc" || parameter[1].trim() === "string" - || parameter[1].split("|")[1] || parameter[1].trim() === "number" - || parameter[1].trim() === "boolean") { - - if ((!!!this._paramsNames.includes(parameter[0].trim()) && - this.dataDoc[parameter[0].trim()] === undefined) || parameter[0].trim() === this._paramsNames[this._paramsNames.length - i - 1]) { - this._errorMessage = ""; - this._paramsNames[this._paramsNames.length - i - 1] = parameter[0].trim(); - this._paramsTypes[this._paramsTypes.length - i - 1] = parameter[1].trim(); - this._paramsValues[this._paramsValues.length - i - 1] = "undefined"; - this.compileParams[i] = value; - return true; - - } else { - this._errorMessage = "this name has already been used"; - return false; - } - } else { - this._errorMessage = "this type is not supported"; - return false; - } - } else { - this._errorMessage = "must set type of parameter"; - return false; - } - } else { - this.onDelete(i); + renderNumber(parameter: string) { + return
+ StrCast(this.dataDoc[parameter]) ?? "undefined"} + SetValue={action((value: string) => { + if (value && value !== " ") { + if (parseInt(value)) { + this._errorMessage = ""; + this.dataDoc[parameter] = parseInt(value); return true; } - }} - /> -
- ); - - - const settingParams = !this._paramsNames ? (null) : this._paramsNames.map((parameter: string, i: number) => -
e.key === "Enter" && this._overlayDisposer?.()} - > - - {this._paramsTypes[i] === "Doc" ?
-
- -
- {parameter + ":" + this._paramsTypes[i] + " = "} -
- -
ele && this.createDashEventsTarget(ele, (e, de) => this.onDrop(e, de, this._paramsNames[i]))} - onFocus={this.onFocus}> - this._paramsValues[i]} - SetValue={action((value: string) => { - const script = CompileScript( - value, { - addReturn: true, - typecheck: false, - transformer: DocumentIconContainer.getTransformer(), - params: { makeInterface: "any" } - }); - const results = script.compiled && script.run(); - if (results && results.success) { - this._errorMessage = ""; - this.dataDoc[parameter] = results.result; - this._paramsValues[i] = results.result.title; - return true; - } else { - this._errorMessage = "invalid document"; - return false; - } - }) - } - /> -
-
-
: null} - - - {this._paramsTypes[i] === "string" ?
-
-
- {parameter + ":" + this._paramsTypes[i] + " = "} -
-
- StrCast(this.dataDoc[parameter]) ?? "undefined"} - SetValue={action((value: string) => { - if (value !== "" && value !== " ") { - this._errorMessage = ""; - this.dataDoc[parameter] = value; - this._paramsValues[i] = value; - return true; - } - return false; - })} - /> -
-
-
: null} - - {this._paramsTypes[i] === "number" ?
-
-
- {parameter + ":" + this._paramsTypes[i] + " = "} -
-
- StrCast(this.dataDoc[parameter]) ?? "undefined"} - SetValue={action((value: string) => { - if (value !== "" && value !== " ") { - if (parseInt(value)) { - this._errorMessage = ""; - this.dataDoc[parameter] = parseInt(value); - this._paramsValues[i] = StrCast(parseInt(value)); - return true; - } else { - this._errorMessage = "not a number"; - return false; - } - } else { - return false; - } - })} - /> -
-
-
: null} - - {this._paramsTypes[i]?.split("|")[1] ?
-
-
- {parameter + ":" + this._paramsTypes[i] + " = "} -
- -
-
-
- -
-
-
-
-
: null} - - {this._paramsTypes[i] === "boolean" ?
-
-
- {parameter + ":" + this._paramsTypes[i] + " = "} -
-
-
-
- -
-
-
-
-
: null} - - + this._errorMessage = "not a number"; + } + return false; + })} + /> +
; + } + renderEnum(parameter: string, types: string[]) { + return
+
+
+ +
- ); - - const scriptingInputs =
this.props.isSelected(true) && e.stopPropagation()} > -
- -