diff --git a/playwright-tests/data/actionBlockElements.json b/playwright-tests/data/actionBlockElements.json index bf41494b..b7b94957 100644 --- a/playwright-tests/data/actionBlockElements.json +++ b/playwright-tests/data/actionBlockElements.json @@ -4,9 +4,9 @@ }, "variables": { "Lookup": ["source", "input", "output", "destination", "addNewPair"], - "Global": ["Commit", "var", "i", "addNewPair"], - "Locals": ["Commit", "var", "i", "addNewPair"], - "Self": ["Commit", "var", "i", "addNewPair"] + "Global": ["name", "value", "addNewPair"], + "Locals": ["name", "value", "addNewPair"], + "Self": ["name", "value", "addNewPair"] }, "led": { "Start Animation": ["ledNumber", "Layer", "Phase", "Rate", "Shape"], diff --git a/playwright-tests/data/actionBlockLocators.js b/playwright-tests/data/actionBlockLocators.js index 65743dc2..817fc266 100644 --- a/playwright-tests/data/actionBlockLocators.js +++ b/playwright-tests/data/actionBlockLocators.js @@ -23,28 +23,25 @@ export const blocks = (page) => ({ Global: { block: page.getByText("Global"), elements: { - Commit: page.getByRole("button", { name: "Commit" }), - var: page.getByPlaceholder("variable name"), - i: page.locator("#monaco_container"), - addNewPair: page.getByText("Add global variable..."), + name: page.getByTestId("variable-name"), + value: page.getByTestId("variable-value"), + addNewPair: page.getByTestId("add-variable"), }, }, Locals: { block: page.getByText("Locals"), elements: { - Commit: page.getByRole("button", { name: "Commit" }), - var: page.getByPlaceholder("variable name"), - i: page.locator("#monaco_container"), - addNewPair: page.getByText("Add local variable..."), + name: page.getByTestId("variable-name"), + value: page.getByTestId("variable-value"), + addNewPair: page.getByTestId("add-variable"), }, }, Self: { block: page.getByText("Self"), elements: { - Commit: page.getByRole("button", { name: "Commit" }), - var: page.getByPlaceholder("variable name"), - i: page.locator("#monaco_container"), - addNewPair: page.getByText("Add self variable..."), + name: page.getByTestId("variable-name"), + value: page.getByTestId("variable-value"), + addNewPair: page.getByTestId("add-variable"), }, }, }, diff --git a/src/renderer/config-blocks/VarGlobal.svelte b/src/renderer/config-blocks/VarGlobal.svelte index 1f05e066..c80f9006 100644 --- a/src/renderer/config-blocks/VarGlobal.svelte +++ b/src/renderer/config-blocks/VarGlobal.svelte @@ -22,114 +22,51 @@ }; - - - - -
- {#if variableNameError} -
Variable name error!
- {/if} - {#if error_messsage !== ""} -
{error_messsage}
- {/if} - {#key commitState} -
+
+
+ Global Variables: + Error: {errorText} - {commitState ? "Unsaved changes!" : "Synced with Grid!"} -
- {/key} - {#if parenthesisError} -
Parenthesis must be closed!
- {/if} - -
- -
- {#each scriptSegments as script, i (i)} -
-
- { - saveChangesOnInput(e.target.value, i, "variable"); - }} - /> -
-
-
- {#key rerenderList} - { - saveChangesOnInput(e.detail.script, i, "value"); - }} - action={config} - {sidebarWidth} - value={script.value} - /> - {/key} +
+ +
+ {#each scriptSegments as script, i} +
+
+ { + return new Validator(e).NotEmpty().Result(); + }} + on:validator={(e) => { + const data = e.detail; + dispatch("validator", data); + }} + on:input={(e) => { + sendData(); + }} + on:change={() => { + dispatch("sync"); + }} + />
-
-
- {#if i !== 0} - - {:else} - - {/if} + + + +
-
- {/each} -
- -
- -
+ {/each} +
- - +
+ +
- + +
+ diff --git a/src/renderer/config-blocks/VarLocals.svelte b/src/renderer/config-blocks/VarLocals.svelte index 0d380a15..2c96da31 100644 --- a/src/renderer/config-blocks/VarLocals.svelte +++ b/src/renderer/config-blocks/VarLocals.svelte @@ -22,119 +22,64 @@ }; - - - - -
- {#if variableNameError} -
Variable name error!
- {/if} - {#if error_messsage !== ""} -
{error_messsage}
- {/if} - {#key commitState} -
+
+
+ Local Variables: + Error: {errorText} - {commitState ? "Unsaved changes!" : "Synced with Grid!"} -
- {/key} - {#if parenthesisError} -
Parenthesis must be closed!
- {/if} - -
- -
- {#each scriptSegments as script, i (i)} -
-
- { - saveChangesOnInput(e.target.value, i, "variable"); - }} - /> -
-
-
- {#key rerenderList} - { - saveChangesOnInput(e.detail.script, i, "value"); - }} - action={config} - {sidebarWidth} - value={script.value} - /> - {/key} +
+ +
+ {#each scriptSegments as script, i} +
+
+ { + return new Validator(e).NotEmpty().Result(); + }} + on:validator={(e) => { + const data = e.detail; + dispatch("validator", data); + }} + on:input={(e) => { + sendData(); + }} + on:change={() => { + dispatch("sync"); + }} + />
-
-
- {#if i !== 0} - - {:else} - - {/if} + + + +
-
- {/each} -
+ {/each} +
-
- -
+
+ +
- - - - + +
+ diff --git a/src/renderer/config-blocks/VarSelf.svelte b/src/renderer/config-blocks/VarSelf.svelte index 04f20072..34d3f84b 100644 --- a/src/renderer/config-blocks/VarSelf.svelte +++ b/src/renderer/config-blocks/VarSelf.svelte @@ -22,115 +22,61 @@ }; - - - - -
- {#if variableNameError} -
Variable name error!
- {/if} - {#if error_messsage !== ""} -
{error_messsage}
- {/if} - {#key commitState} -
+
+
+ Self Variables: + Error: {errorText} - {commitState ? "Unsaved changes!" : "Synced with Grid!"} -
- {/key} - {#if parenthesisError} -
Parenthesis must be closed!
- {/if} - -
- -
- {#each scriptSegments as script, i (i)} -
-
- { - saveChangesOnInput(e.target.value, i, "variable"); - }} - /> -
-
-
- {#key rerenderList} - { - saveChangesOnInput(e.detail.script, i, "value"); - }} - action={config} - {sidebarWidth} - value={script.value} - /> - {/key} +
+ +
+ {#each scriptSegments as script, i} +
+
+ { + return new Validator(e).NotEmpty().Result(); + }} + on:validator={(e) => { + const data = e.detail; + dispatch("validator", data); + }} + on:input={(e) => { + sendData(); + }} + on:change={() => { + dispatch("sync"); + }} + />
-
-
- {#if i !== 0} - - {:else} - - {/if} + + + +
-
- {/each} -
+ {/each} +
-
- -
- - - +
+ +
- + +
+ diff --git a/src/renderer/lib/_utils.ts b/src/renderer/lib/_utils.ts index bfb8a1f5..8bbe4ee2 100644 --- a/src/renderer/lib/_utils.ts +++ b/src/renderer/lib/_utils.ts @@ -1,4 +1,7 @@ import convert from "color-convert"; +import { checkVariableName } from "../validators/local_validator.mjs"; +import { parenthesis } from "../config-blocks/_validators"; +import { find_forbidden_identifiers } from "../runtime/monaco-helper"; import { grid } from "@intechstudio/grid-protocol"; export namespace Grid { @@ -174,6 +177,55 @@ export namespace Grid { return new RGB(parseInt(r), parseInt(g), parseInt(b)); } + export namespace VariableBlock { + export type Error = { + value: boolean; + text: string; + }; + + export type ScriptSegment = { variable: string; value: string }; + + export function localArrayToScript(arr: ScriptSegment[]) { + let script = [ + "local ", + arr.map((e) => e.variable).join(","), + "=", + arr.map((e) => e.value).join(","), + ].join(""); + return script; + } + + export function getError(scriptSegments: ScriptSegment[]): Error { + let variableNameValidity = []; + scriptSegments.forEach((s) => { + variableNameValidity.push(checkVariableName(s.variable)); + }); + + const script = localArrayToScript(scriptSegments); + + if (variableNameValidity.includes(false)) { + return { value: true, text: "Invalid variable name!" }; + } + + if (!parenthesis(script)) { + return { value: true, text: "Parenthesis must be closed!" }; + } + + let forbiddenList = find_forbidden_identifiers(script); + + if (forbiddenList.length > 0) { + const uniqueForbiddenList = [...new Set(forbiddenList)]; + const readable = uniqueForbiddenList.toString().replace(",", ", "); + return { + value: true, + text: "Reserved identifiers [" + readable + "] cannot be used!", + }; + } + + return { value: false, text: "OK" }; + } + } + export namespace Protocol { export const scriptStart = ""; diff --git a/src/renderer/main/_actions/move.action.ts b/src/renderer/main/_actions/move.action.ts index 13010c0e..dce179af 100644 --- a/src/renderer/main/_actions/move.action.ts +++ b/src/renderer/main/_actions/move.action.ts @@ -1,6 +1,10 @@ import { get, writable, type Writable } from "svelte/store"; import { GridAction, GridEvent } from "../../runtime/runtime"; -import { dropActions, removeActions } from "../../runtime/operations"; +import { + dropActions, + removeActions, + syncWithGrid, +} from "../../runtime/operations"; export const draggedActions: Writable = writable([]); export type DropTarget = { event: GridEvent; index: number }; @@ -175,7 +179,9 @@ export function dropzone(node: HTMLElement, params: DropParameters) { function handleDropAction(e: DropActionEvent) { const { dropped } = e.detail; const { event, index } = params; - dropActions(event, index, dropped); + dropActions(event, index, dropped).then(() => { + event.sendToGrid(); + }); } function handleMouseOver() { diff --git a/src/renderer/runtime/operations.ts b/src/renderer/runtime/operations.ts index 95d0cf41..5b66deb2 100644 --- a/src/renderer/runtime/operations.ts +++ b/src/renderer/runtime/operations.ts @@ -426,7 +426,7 @@ export function dropActions( target: GridEvent, index: number, actions: GridAction[] -) { +): Promise { let targetActions = actions.filter((e) => e.parent === target); let targetIndexes = targetActions.map((action) => target.config.findIndex((e) => e.id === action.id) @@ -435,7 +435,7 @@ export function dropActions( const targetMaxIndex = Math.max(...targetIndexes); if (index >= targetMinIndex && index <= targetMaxIndex + 1) { - return; // Invalid drop zone, return the original list + return Promise.reject("Invalid drop zone!"); } for (const action of actions) { @@ -449,4 +449,5 @@ export function dropActions( } target.insert(index, ...actions); + return Promise.resolve(); }