import { action, observable, computed, _allowStateChangesInsideComputed, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as React from "react"; 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 { InteractionUtils } from "../../util/InteractionUtils"; import { CompileScript, ScriptSucccess, ScriptParam } from "../../util/Scripting"; 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"; const ScriptingSchema = createSchema({}); type ScriptingDocument = makeInterface<[typeof ScriptingSchema, typeof documentSchema]>; const ScriptingDocument = makeInterface(ScriptingSchema, documentSchema); @observer export class ScriptingBox extends ViewBoxAnnotatableComponent(ScriptingDocument) { private dropDisposer?: DragManager.DragDropDisposer; protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined; public static LayoutString(fieldStr: string) { return FieldView.LayoutString(ScriptingBox, fieldStr); } private _overlayDisposer?: () => 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 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(); @action componentDidMount() { this.rawScript = ScriptCast(this.dataDoc[this.props.fieldKey])?.script?.originalScript || this.rawScript; } componentWillUnmount() { this._overlayDisposer?.(); } @action onFinish = () => { const result = CompileScript(this.rawScript, {}); this.rootDoc.layoutKey = "layout"; 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; } } @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 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); } else { this.onError(result.errors); } this.dataDoc.documentText = this.rawScript; } @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); 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")); } @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; } @action onEdit = () => { this._applied = false; } onFocus = () => { this._overlayDisposer?.(); this._overlayDisposer = OverlayView.Instance.addElement(, { x: 0, y: 0 }); } @action onDrop = (e: Event, de: DragManager.DropEvent, fieldKey: string) => { 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); } e.stopPropagation(); } @action onDelete = (num: number) => { 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); } @action viewChanged = (e: React.ChangeEvent, i: number, 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); 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" }); } 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; } } 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); 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}
); const scriptingInputs =
this.props.isSelected(true) && e.stopPropagation()} >