From 505924405526c24664e0d2706d59d8feb2301ec3 Mon Sep 17 00:00:00 2001 From: Julian Wielga Date: Mon, 23 Jun 2025 13:00:29 +0200 Subject: [PATCH 01/10] moved dnd provider --- .../client/src/containers/NussknackerApp.tsx | 53 ++++++++++--------- .../client/src/containers/Visualization.tsx | 8 ++- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/designer/client/src/containers/NussknackerApp.tsx b/designer/client/src/containers/NussknackerApp.tsx index e99628142dd..64d68d1dfd5 100644 --- a/designer/client/src/containers/NussknackerApp.tsx +++ b/designer/client/src/containers/NussknackerApp.tsx @@ -1,6 +1,7 @@ -import { css } from "@emotion/css"; import { isEmpty } from "lodash"; +import { HTML5toTouch } from "rdndmb-html5-to-touch"; import React from "react"; +import { DndProvider } from "react-dnd-multi-backend"; import { useSelector } from "react-redux"; import { Outlet } from "react-router-dom"; @@ -28,35 +29,37 @@ export function NussknackerApp() { return ( <> - -
+ - -
- -
-
- -
+
+ +
+ +
+
+ + - - - + + + + ); diff --git a/designer/client/src/containers/Visualization.tsx b/designer/client/src/containers/Visualization.tsx index f12ee35e4d4..10f4474aca4 100644 --- a/designer/client/src/containers/Visualization.tsx +++ b/designer/client/src/containers/Visualization.tsx @@ -1,8 +1,6 @@ import { useWindowManager } from "@touk/window-manager"; import { isEmpty } from "lodash"; -import { HTML5toTouch } from "rdndmb-html5-to-touch"; -import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { DndProvider } from "react-dnd-multi-backend"; +import React, { useCallback, useEffect, useMemo } from "react"; import { useErrorBoundary } from "react-error-boundary"; import { useDispatch, useSelector } from "react-redux"; import { useNavigate, useSearchParams } from "react-router-dom"; @@ -208,7 +206,7 @@ function Visualization() { const [Portal, portalRef] = usePortal(); return ( - + <> {isEmpty(processDefinitionData) ? null : } @@ -225,7 +223,7 @@ function Visualization() {
- + ); } From 6fa533a8c806dc4aeca3cf3460cb30be8585d44b Mon Sep 17 00:00:00 2001 From: Julian Wielga Date: Mon, 23 Jun 2025 13:02:00 +0200 Subject: [PATCH 02/10] moved dnd provider --- designer/client/src/containers/Visualization.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/designer/client/src/containers/Visualization.tsx b/designer/client/src/containers/Visualization.tsx index 10f4474aca4..522b4823bc3 100644 --- a/designer/client/src/containers/Visualization.tsx +++ b/designer/client/src/containers/Visualization.tsx @@ -1,6 +1,6 @@ import { useWindowManager } from "@touk/window-manager"; import { isEmpty } from "lodash"; -import React, { useCallback, useEffect, useMemo } from "react"; +import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useErrorBoundary } from "react-error-boundary"; import { useDispatch, useSelector } from "react-redux"; import { useNavigate, useSearchParams } from "react-router-dom"; From bd84719ce877afa8d416029e525da92461bd1d74 Mon Sep 17 00:00:00 2001 From: Julian Wielga Date: Mon, 23 Jun 2025 14:25:21 +0200 Subject: [PATCH 03/10] moved dnd provider --- designer/client/src/containers/NussknackerApp.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/designer/client/src/containers/NussknackerApp.tsx b/designer/client/src/containers/NussknackerApp.tsx index 64d68d1dfd5..4231e9dacf9 100644 --- a/designer/client/src/containers/NussknackerApp.tsx +++ b/designer/client/src/containers/NussknackerApp.tsx @@ -1,3 +1,4 @@ +import { css } from "@emotion/css"; import { isEmpty } from "lodash"; import { HTML5toTouch } from "rdndmb-html5-to-touch"; import React from "react"; From dbe9029b173fd978e0bd4f8a47ee747cd7d9c0eb Mon Sep 17 00:00:00 2001 From: Julian Wielga Date: Mon, 23 Jun 2025 15:24:24 +0200 Subject: [PATCH 04/10] moved dnd provider --- designer/client/src/components/ComponentDragPreview.tsx | 2 +- designer/client/src/components/DndTypes.tsx | 4 ++++ designer/client/src/components/graph/ProcessGraph.tsx | 2 +- designer/client/src/components/toolbars/creator/Tool.tsx | 7 ++----- 4 files changed, 8 insertions(+), 7 deletions(-) create mode 100644 designer/client/src/components/DndTypes.tsx diff --git a/designer/client/src/components/ComponentDragPreview.tsx b/designer/client/src/components/ComponentDragPreview.tsx index 70cb72f6028..79866985217 100644 --- a/designer/client/src/components/ComponentDragPreview.tsx +++ b/designer/client/src/components/ComponentDragPreview.tsx @@ -7,9 +7,9 @@ import { useDebouncedValue } from "rooks"; import type { NodeType } from "../types"; import type { ComponentPreviewProps } from "./ComponentPreview"; import { ComponentPreview } from "./ComponentPreview"; +import { DndTypes } from "./DndTypes"; import { StickyNoteType } from "./graph/utils/stickyNotesUtils"; import { StickyNotePreview } from "./StickyNotePreview"; -import { DndTypes } from "./toolbars/creator/Tool"; function useNotNull(value: T) { const [current, setCurrent] = useState(() => value); diff --git a/designer/client/src/components/DndTypes.tsx b/designer/client/src/components/DndTypes.tsx new file mode 100644 index 00000000000..a5a385b6155 --- /dev/null +++ b/designer/client/src/components/DndTypes.tsx @@ -0,0 +1,4 @@ +export const DndTypes = { + ELEMENT: "element", + VALUE: "value", +}; diff --git a/designer/client/src/components/graph/ProcessGraph.tsx b/designer/client/src/components/graph/ProcessGraph.tsx index e7810fb42ee..6331b31c3cd 100644 --- a/designer/client/src/components/graph/ProcessGraph.tsx +++ b/designer/client/src/components/graph/ProcessGraph.tsx @@ -26,8 +26,8 @@ import { fetchScenarios, getScenariosNames } from "../../reducers/scenarios"; import { getLayout, getProcessCounts, getScenario } from "../../reducers/selectors/graph"; import type { Capabilities } from "../../reducers/selectors/other"; import type { NodeType } from "../../types"; +import { DndTypes } from "../DndTypes"; import type { Scenario } from "../Process/types"; -import { DndTypes } from "../toolbars/creator/Tool"; import { jsonToFileInFormData } from "./createFragment"; import { RECT_HEIGHT, RECT_WIDTH } from "./EspNode/esp"; import type { Graph } from "./Graph"; diff --git a/designer/client/src/components/toolbars/creator/Tool.tsx b/designer/client/src/components/toolbars/creator/Tool.tsx index 104e5282a8d..50e2aabb764 100644 --- a/designer/client/src/components/toolbars/creator/Tool.tsx +++ b/designer/client/src/components/toolbars/creator/Tool.tsx @@ -5,14 +5,11 @@ import { useDrag } from "react-dnd"; import { getEmptyImage } from "react-dnd-html5-backend"; import type { NodeType } from "../../../types"; +import { DndTypes } from "../../DndTypes"; import { InfoTooltip } from "../../graph/node-modal/editors/InfoTooltip"; import { ComponentIcon } from "./ComponentIcon"; import { SearchHighlighter } from "./SearchHighlighter"; -export const DndTypes = { - ELEMENT: "element", -}; - type OwnProps = { nodeModel: NodeType; label: string; @@ -21,7 +18,7 @@ type OwnProps = { tooltip?: string; }; -export default function Tool(props: OwnProps): JSX.Element { +export default function Tool(props: OwnProps): React.JSX.Element { const { label, nodeModel, highlights = [], disabled, tooltip } = props; const [, drag, preview] = useDrag(() => ({ type: DndTypes.ELEMENT, From 4b4ea4c896243e7ce1fb02bf549dec1369ce02f7 Mon Sep 17 00:00:00 2001 From: Julian Wielga Date: Thu, 26 Jun 2025 19:02:12 +0200 Subject: [PATCH 05/10] (semi) final commit --- designer/client/package-lock.json | 249 ++++++++++++++++++ designer/client/package.json | 4 +- designer/client/src/actions/nk/editNode.ts | 2 - designer/client/src/common/SVGUtils.ts | 2 +- .../src/components/ComponentDragPreview.tsx | 9 +- .../src/components/ValueDragPreview.tsx | 113 ++++++++ .../editors/expression/AceWithSettings.tsx | 36 ++- .../editors/expression/AceWrapper.tsx | 5 +- .../editors/expression/useAceDndTarget.tsx | 145 ++++++++++ .../graph/node-modal/io/ContextTree.tsx | 213 +++++++++++---- .../node-modal/io/InputOutputContent.tsx | 5 +- .../node-modal/io/VariableContextTree.tsx | 4 +- .../client/src/containers/theme/styles.ts | 5 + .../src/helpers/memoizeByArgsWithTTL.ts | 4 +- 14 files changed, 725 insertions(+), 71 deletions(-) create mode 100644 designer/client/src/components/ValueDragPreview.tsx create mode 100644 designer/client/src/components/graph/node-modal/editors/expression/useAceDndTarget.tsx diff --git a/designer/client/package-lock.json b/designer/client/package-lock.json index 187699a2c99..2859ebc91f1 100644 --- a/designer/client/package-lock.json +++ b/designer/client/package-lock.json @@ -134,6 +134,7 @@ "@fontsource/inter": "5.0.16", "@fontsource/roboto-mono": "4.5.8", "@frsource/cypress-plugin-visual-regression-diff": "3.2.3", + "@microlink/react-json-view": "1.26.2", "@pmmmwh/react-refresh-webpack-plugin": "0.5.4", "@svgr/webpack": "8.0.1", "@testing-library/dom": "9.3.1", @@ -216,6 +217,7 @@ "postcss-move-props-to-bg-image-query": "4.0.0", "prettier": "2.8.8", "raw-loader": "4.0.2", + "react-json-tree": "0.20.0", "react-refresh": "0.11.0", "react-scrollbars-custom": "4.1.1", "redux-mock-store": "1.5.4", @@ -4702,6 +4704,24 @@ "react": ">=16.3.0" } }, + "node_modules/@microlink/react-json-view": { + "version": "1.26.2", + "resolved": "https://registry.npmjs.org/@microlink/react-json-view/-/react-json-view-1.26.2.tgz", + "integrity": "sha512-NamaHDT21njvbg2RZQq+rnu+owlPyj5lnUdVH5ZtChfTX+75QD2EGnccB1gs0De42jdPj77UQHYLr7d4J46IYA==", + "dev": true, + "dependencies": { + "react-base16-styling": "~0.9.0", + "react-lifecycles-compat": "~3.0.4", + "react-textarea-autosize": "~8.5.7" + }, + "engines": { + "node": ">=17" + }, + "peerDependencies": { + "react": ">= 15", + "react-dom": ">= 15" + } + }, "node_modules/@mui/base": { "version": "5.0.0-beta.40", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", @@ -7016,6 +7036,12 @@ "@types/underscore": "*" } }, + "node_modules/@types/base16": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/base16/-/base16-1.0.5.tgz", + "integrity": "sha512-OzOWrTluG9cwqidEzC/Q6FAmIPcnZfm8BFRlIx0+UIUqnuAmi5OS88O0RpT3Yz6qdmqObvUhasrbNsCofE4W9A==", + "dev": true + }, "node_modules/@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -9396,6 +9422,12 @@ "node": ">=0.10.0" } }, + "node_modules/base16": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", + "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==", + "dev": true + }, "node_modules/base64-js": { "version": "1.5.1", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", @@ -20105,6 +20137,12 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, + "node_modules/lodash.curry": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", + "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==", + "dev": true + }, "node_modules/lodash.debounce": { "version": "4.0.8", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" @@ -23652,6 +23690,31 @@ "react-dom": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-base16-styling": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.9.1.tgz", + "integrity": "sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.16.7", + "@types/base16": "^1.0.2", + "@types/lodash": "^4.14.178", + "base16": "^1.0.0", + "color": "^3.2.1", + "csstype": "^3.0.10", + "lodash.curry": "^4.1.1" + } + }, + "node_modules/react-base16-styling/node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "node_modules/react-clientside-effect": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz", @@ -23980,6 +24043,75 @@ "version": "16.13.1", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-json-tree": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/react-json-tree/-/react-json-tree-0.20.0.tgz", + "integrity": "sha512-h+f9fUNAxzBx1rbrgUF7+zSWKGHDtt2VPYLErIuB0JyKGnWgFMM21ksqQyb3EXwXNnoMW2rdE5kuAaubgGOx2Q==", + "dev": true, + "dependencies": { + "@types/lodash": "^4.17.15", + "react-base16-styling": "^0.10.0" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-json-tree/node_modules/@types/lodash": { + "version": "4.17.18", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.18.tgz", + "integrity": "sha512-KJ65INaxqxmU6EoCiJmRPZC9H9RVWCRd349tXM2M3O5NA7cY6YL7c0bHAHQ93NOfTObEQ004kd2QVHs/r0+m4g==", + "dev": true + }, + "node_modules/react-json-tree/node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/react-json-tree/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/react-json-tree/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/react-json-tree/node_modules/react-base16-styling": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.10.0.tgz", + "integrity": "sha512-H1k2eFB6M45OaiRru3PBXkuCcn2qNmx+gzLb4a9IPMR7tMH8oBRXU5jGbPDYG1Hz+82d88ED0vjR8BmqU3pQdg==", + "dev": true, + "dependencies": { + "@types/lodash": "^4.17.0", + "color": "^4.2.3", + "csstype": "^3.1.3", + "lodash-es": "^4.17.21" + } + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "dev": true + }, "node_modules/react-markdown": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz", @@ -33318,6 +33450,17 @@ "react-is": "^16.12.0" } }, + "@microlink/react-json-view": { + "version": "1.26.2", + "resolved": "https://registry.npmjs.org/@microlink/react-json-view/-/react-json-view-1.26.2.tgz", + "integrity": "sha512-NamaHDT21njvbg2RZQq+rnu+owlPyj5lnUdVH5ZtChfTX+75QD2EGnccB1gs0De42jdPj77UQHYLr7d4J46IYA==", + "dev": true, + "requires": { + "react-base16-styling": "~0.9.0", + "react-lifecycles-compat": "~3.0.4", + "react-textarea-autosize": "~8.5.7" + } + }, "@mui/base": { "version": "5.0.0-beta.40", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", @@ -34729,6 +34872,12 @@ "@types/underscore": "*" } }, + "@types/base16": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/base16/-/base16-1.0.5.tgz", + "integrity": "sha512-OzOWrTluG9cwqidEzC/Q6FAmIPcnZfm8BFRlIx0+UIUqnuAmi5OS88O0RpT3Yz6qdmqObvUhasrbNsCofE4W9A==", + "dev": true + }, "@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -36647,6 +36796,12 @@ } } }, + "base16": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", + "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==", + "dev": true + }, "base64-js": { "version": "1.5.1", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", @@ -44674,6 +44829,12 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, + "lodash.curry": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", + "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==", + "dev": true + }, "lodash.debounce": { "version": "4.0.8", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" @@ -47256,6 +47417,33 @@ "prop-types": "^15.8.1" } }, + "react-base16-styling": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.9.1.tgz", + "integrity": "sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.16.7", + "@types/base16": "^1.0.2", + "@types/lodash": "^4.14.178", + "base16": "^1.0.0", + "color": "^3.2.1", + "csstype": "^3.0.10", + "lodash.curry": "^4.1.1" + }, + "dependencies": { + "color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "requires": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + } + } + }, "react-clientside-effect": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz", @@ -47478,6 +47666,67 @@ "version": "16.13.1", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-json-tree": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/react-json-tree/-/react-json-tree-0.20.0.tgz", + "integrity": "sha512-h+f9fUNAxzBx1rbrgUF7+zSWKGHDtt2VPYLErIuB0JyKGnWgFMM21ksqQyb3EXwXNnoMW2rdE5kuAaubgGOx2Q==", + "dev": true, + "requires": { + "@types/lodash": "^4.17.15", + "react-base16-styling": "^0.10.0" + }, + "dependencies": { + "@types/lodash": { + "version": "4.17.18", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.18.tgz", + "integrity": "sha512-KJ65INaxqxmU6EoCiJmRPZC9H9RVWCRd349tXM2M3O5NA7cY6YL7c0bHAHQ93NOfTObEQ004kd2QVHs/r0+m4g==", + "dev": true + }, + "color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "requires": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "react-base16-styling": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.10.0.tgz", + "integrity": "sha512-H1k2eFB6M45OaiRru3PBXkuCcn2qNmx+gzLb4a9IPMR7tMH8oBRXU5jGbPDYG1Hz+82d88ED0vjR8BmqU3pQdg==", + "dev": true, + "requires": { + "@types/lodash": "^4.17.0", + "color": "^4.2.3", + "csstype": "^3.1.3", + "lodash-es": "^4.17.21" + } + } + } + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "dev": true + }, "react-markdown": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz", diff --git a/designer/client/package.json b/designer/client/package.json index a0268dcf26a..e40b1ddb54f 100644 --- a/designer/client/package.json +++ b/designer/client/package.json @@ -127,6 +127,7 @@ "@fontsource/inter": "5.0.16", "@fontsource/roboto-mono": "4.5.8", "@frsource/cypress-plugin-visual-regression-diff": "3.2.3", + "@microlink/react-json-view": "1.26.2", "@pmmmwh/react-refresh-webpack-plugin": "0.5.4", "@svgr/webpack": "8.0.1", "@testing-library/dom": "9.3.1", @@ -209,6 +210,7 @@ "postcss-move-props-to-bg-image-query": "4.0.0", "prettier": "2.8.8", "raw-loader": "4.0.2", + "react-json-tree": "0.20.0", "react-refresh": "0.11.0", "react-scrollbars-custom": "4.1.1", "redux-mock-store": "1.5.4", @@ -275,7 +277,7 @@ "start:backend-remote": "npm run clean-translations && webpack serve", "start:backend-staging": "BACKEND_DOMAIN=https://staging.nussknacker.io npm run start:backend-remote", "start:backend-demo": "BACKEND_DOMAIN=https://demo.nussknacker.io npm run start:backend-remote", - "start:backend-cloud": "BACKEND_DOMAIN=https://light-pink-silkworm-nussknacker.staging-cloud.nussknacker.io npm run start:backend-remote", + "start:backend-cloud": "BACKEND_DOMAIN=https://pink-snakes-nussknacker.staging-cloud.nussknacker.io npm run start:backend-remote", "backend:docker": "docker-compose kill && docker-compose rm -f -v && docker-compose up --no-recreate > /dev/null", "pretest": "npm run check", "test:types": "tstyche", diff --git a/designer/client/src/actions/nk/editNode.ts b/designer/client/src/actions/nk/editNode.ts index ff5728e8d6c..e01bac92946 100644 --- a/designer/client/src/actions/nk/editNode.ts +++ b/designer/client/src/actions/nk/editNode.ts @@ -8,7 +8,6 @@ import { getProcessDefinitionData } from "../../reducers/selectors/processDefini import type { Edge, NodeType, ScenarioGraph, ValidationResult } from "../../types"; import type { ThunkAction } from "../reduxTypes"; import { calculateProcessAfterChange } from "./calculateProcessAfterChange"; -import { clearProcessCounts } from "./displayProcessCounts"; export type EditNodeAction = { type: "EDIT_NODE"; @@ -34,7 +33,6 @@ export function editNode(scenarioBefore: Scenario, before: NodeType, after: Node const scenarioGraph = await dispatch(calculateProcessAfterChange(scenarioBefore, before, after, outputEdges)); const response = await HttpService.validateProcess(scenarioBefore.name, scenarioBefore.name, scenarioGraph); - dispatch(clearProcessCounts()); dispatch({ type: "EDIT_NODE", before, diff --git a/designer/client/src/common/SVGUtils.ts b/designer/client/src/common/SVGUtils.ts index fd00dec490c..e9d2dcadfef 100644 --- a/designer/client/src/common/SVGUtils.ts +++ b/designer/client/src/common/SVGUtils.ts @@ -40,6 +40,6 @@ export function toXml(node: Node) { return window.XMLSerializer ? new XMLSerializer().serializeToString(node) : xmlSerializerForIE(node); } -export function svgTowDataURL(svgStr: string) { +export function svgToDataURL(svgStr: string) { return `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(svgStr)))}`; } diff --git a/designer/client/src/components/ComponentDragPreview.tsx b/designer/client/src/components/ComponentDragPreview.tsx index 79866985217..e7b072f3d2e 100644 --- a/designer/client/src/components/ComponentDragPreview.tsx +++ b/designer/client/src/components/ComponentDragPreview.tsx @@ -1,5 +1,4 @@ -import { css } from "@emotion/css"; -import React, { forwardRef, useEffect, useMemo, useState } from "react"; +import React, { forwardRef, useEffect, useMemo } from "react"; import { useDragDropManager, useDragLayer } from "react-dnd"; import { createPortal } from "react-dom"; import { useDebouncedValue } from "rooks"; @@ -11,7 +10,7 @@ import { DndTypes } from "./DndTypes"; import { StickyNoteType } from "./graph/utils/stickyNotesUtils"; import { StickyNotePreview } from "./StickyNotePreview"; -function useNotNull(value: T) { +export function useNotNull(value: T) { const [current, setCurrent] = useState(() => value); useEffect(() => { if (!value) return; @@ -34,8 +33,8 @@ export const ComponentDragPreview = forwardRef nu const manager = useDragDropManager(); const monitor = manager.getMonitor(); const { currentOffset, active, data } = useDragLayer((monitor) => ({ - data: monitor.getItem(), - active: monitor.isDragging() && monitor.getItemType() === DndTypes.ELEMENT, + data: monitor.getItemType() === DndTypes.ELEMENT && monitor.getItem(), + active: monitor.getItemType() === DndTypes.ELEMENT && monitor.isDragging(), currentOffset: monitor.getClientOffset(), })); diff --git a/designer/client/src/components/ValueDragPreview.tsx b/designer/client/src/components/ValueDragPreview.tsx new file mode 100644 index 00000000000..dce41e81ff2 --- /dev/null +++ b/designer/client/src/components/ValueDragPreview.tsx @@ -0,0 +1,113 @@ +import { Box, Paper, Typography } from "@mui/material"; +import type { Dispatch, PropsWithChildren, SetStateAction } from "react"; +import React, { createContext, forwardRef, useMemo } from "react"; +import { useDragDropManager, useDragLayer } from "react-dnd"; +import { createPortal } from "react-dom"; + +import { useNotNull } from "./ComponentDragPreview"; +import { DndTypes } from "./DndTypes"; +import type { ExpressionLang } from "./graph/node-modal/editors/expression/types"; +import { getPathString } from "./graph/node-modal/editors/expression/useAceDndTarget"; +import type { SpelDndContext } from "./graph/node-modal/io/ContextTree"; + +export const AcceptedLangCtx = createContext>>(null); + +function KeyPreview({ data }: { data: SpelDndContext }) { + return <>{getPathString(data?.path)}; +} + +function ValuePreview({ data }: { data: SpelDndContext }) { + if (Array.isArray(data.value)) { + const size = data.value.length; + return ( + <> + List ({size} {size === 1 ? "item" : "items"}) + + ); + } + if (typeof data.value === "object") { + const size = Object.keys(data.value).length; + return ( + <> + Record ({size} {size === 1 ? "key" : "keys"}) + + ); + } + return <>{data.value}; +} + +function KeyValuePreview({ data }: { data: SpelDndContext }) { + return ( + + ({ + color: data.type === "key" ? theme.palette.info.main : "inherit", + opacity: data.type === "key" ? 1 : 0.5, + })} + > + + + {data.type === "value" ? ( + ({ + color: theme.palette.info.main, + })} + > + + + ) : null} + + ); +} + +export const ValueDragPreview = forwardRef(function ValueDragPreview({ children }, forwardedRef) { + const manager = useDragDropManager(); + const monitor = manager.getMonitor(); + const targetIds = monitor.getTargetIds(); + + const [_acceptedLang, setAcceptedLang] = useState(null); + const acceptedLang = useMemo(() => { + const isOver = targetIds.some((id) => monitor.isOverTarget(id) && monitor.canDropOnTarget(id)); + if (isOver) { + return _acceptedLang; + } + return null; + }, [_acceptedLang, monitor, targetIds]); + + const { currentOffset, active, data } = useDragLayer((monitor) => ({ + data: monitor.getItemType() === DndTypes.VALUE && (monitor.getItem() as SpelDndContext), + active: monitor.isDragging() && monitor.getItemType() === DndTypes.VALUE, + currentOffset: monitor.getClientOffset(), + })); + + const { x = 0, y = 0 } = useNotNull(currentOffset) || {}; + + const portal = createPortal( + + + , + document.body, + ); + return ( + <> + {children} + {portal} + + ); +}); diff --git a/designer/client/src/components/graph/node-modal/editors/expression/AceWithSettings.tsx b/designer/client/src/components/graph/node-modal/editors/expression/AceWithSettings.tsx index dc6b231337c..f339d0e2063 100644 --- a/designer/client/src/components/graph/node-modal/editors/expression/AceWithSettings.tsx +++ b/designer/client/src/components/graph/node-modal/editors/expression/AceWithSettings.tsx @@ -1,7 +1,8 @@ /* eslint-disable i18next/no-literal-string */ +import { GlobalStyles } from "@mui/material"; import { throttle } from "lodash"; import type { ForwardedRef } from "react"; -import React, { forwardRef, useEffect, useMemo, useRef } from "react"; +import React, { forwardRef, useEffect, useMemo } from "react"; import type ReactAce from "react-ace/lib/ace"; import { useMergeRefs } from "rooks"; @@ -9,6 +10,7 @@ import { useUserSettings } from "../../../../../common/userSettings"; import type { UserSettings } from "../../../../../reducers/userSettings"; import type { AceKeyCommand, AceWrapperProps } from "./AceWrapper"; import AceWrapper from "./AceWrapper"; +import { useAceDndTarget } from "./useAceDndTarget"; export default forwardRef(function AceWithSettings( props: Omit, @@ -64,13 +66,31 @@ export default forwardRef(function AceWithSettings( const mergedRefs = useMergeRefs(editorRef, ref); + const { isOver } = useAceDndTarget(editorRef, props.inputProps.language); + return ( - + <> + {isOver ? ( + ({ + ".ace_ghost_text": { + color: theme.palette.info.main, + fontStyle: "normal", + opacity: 1, + borderBottom: "2px dashed", + }, + })} + /> + ) : null} + + + ); }); diff --git a/designer/client/src/components/graph/node-modal/editors/expression/AceWrapper.tsx b/designer/client/src/components/graph/node-modal/editors/expression/AceWrapper.tsx index 7e7a48f23f2..68dc5391879 100644 --- a/designer/client/src/components/graph/node-modal/editors/expression/AceWrapper.tsx +++ b/designer/client/src/components/graph/node-modal/editors/expression/AceWrapper.tsx @@ -5,8 +5,7 @@ import type { Ace } from "ace-builds"; import { trimStart } from "lodash"; import type { ForwardedRef, ReactNode } from "react"; import React, { forwardRef, useMemo } from "react"; -import type { IAceEditorProps } from "react-ace/lib/ace"; -import type ReactAce from "react-ace/lib/ace"; +import type ReactAce, { IAceEditorProps } from "react-ace/lib/ace"; import type { ICommand } from "react-ace/lib/types"; import type { IAceOptions, IEditorProps } from "react-ace/src/types"; @@ -16,7 +15,7 @@ import type { EditorMode } from "./types"; import { ExpressionLang } from "./types"; export type AceWrapperInputProps = { - language: string; + language: ExpressionLang; readOnly?: boolean; editorMode?: EditorMode; className?: string; diff --git a/designer/client/src/components/graph/node-modal/editors/expression/useAceDndTarget.tsx b/designer/client/src/components/graph/node-modal/editors/expression/useAceDndTarget.tsx new file mode 100644 index 00000000000..dffd5193caa --- /dev/null +++ b/designer/client/src/components/graph/node-modal/editors/expression/useAceDndTarget.tsx @@ -0,0 +1,145 @@ +import type React from "react"; +import { useCallback, useContext, useEffect } from "react"; +import type ReactAce from "react-ace/lib/ace"; +import type { XYCoord } from "react-dnd"; +import { useDragLayer, useDrop } from "react-dnd"; +import type { KeyPath } from "react-json-tree"; + +import { memoizeByArgsWithTTL } from "../../../../../helpers/memoizeByArgsWithTTL"; +import { DndTypes } from "../../../../DndTypes"; +import { AcceptedLangCtx } from "../../../../ValueDragPreview"; +import type { SpelDndContext } from "../../io/ContextTree"; +import { ExpressionLang } from "./types"; + +function jsonToSpelString(value: any): string { + if (value === null) return "null"; + if (typeof value === "string") return `"${value.replace(/"/g, '\\"')}"`; + if (typeof value === "number" || typeof value === "boolean") return String(value); + + if (Array.isArray(value)) { + return `{${value.map(jsonToSpelString).join(",")}}`; + } + + if (typeof value === "object") { + const entries = Object.entries(value).map(([key, val]) => [key.match(/\s/) ? `"${key}"` : key, jsonToSpelString(val)].join(": ")); + return `{${entries.join(", ")}}`; + } + + throw new Error(`Unsupported type: ${typeof value}`); +} + +export const getPathString = memoizeByArgsWithTTL((item: KeyPath = []): string => { + return item + .map((key, index) => { + if (index === 0) return `${key}`; + if (typeof key === "number") return `[${key}]`; + if (key.match(/\s/)) return `["${key}"]`; + return `.${key}`; + }) + .join(""); +}); + +const getTextToInsert = memoizeByArgsWithTTL((item: SpelDndContext, language: ExpressionLang): string => { + switch (item?.type) { + case "key": { + const path = getPathString(item.path); + switch (language) { + case ExpressionLang.JsonTemplate: + case ExpressionLang.SpELTemplate: + return `#{#${path}}`; + case ExpressionLang.SpEL: + return `#${path}`; + default: + return path; + } + } + case "value": { + switch (language) { + case ExpressionLang.SpELTemplate: + case ExpressionLang.SpEL: + return jsonToSpelString(item.value); + } + } + } + switch (typeof item?.value) { + case "string": + case "number": + case "boolean": + return `${item.value}`; + } + return JSON.stringify(item?.value); +}); + +export function useAceDndTarget(editorRef: React.MutableRefObject, language: ExpressionLang) { + const getEditor = useCallback(() => editorRef?.current?.editor, [editorRef]); + + const removePlaceholder = useCallback(() => { + getEditor()?.removeGhostText(); + }, [getEditor]); + + const getText = useCallback((item: SpelDndContext) => getTextToInsert(item, language), [language]); + + const addPlaceholder = useCallback( + (clientOffset: XYCoord, item: SpelDndContext) => { + const editor = getEditor(); + if (!editor) return; + + editor.session.selection.clearSelection(); + const screenCoordinates = editor.renderer.pixelToScreenCoordinates(clientOffset.x, clientOffset.y); + const documentPosition = editor.session.screenToDocumentPosition(screenCoordinates.row, screenCoordinates.column); + editor.setGhostText(getText(item), documentPosition); + }, + [getEditor, getText], + ); + + const insertValue = useCallback( + (clientOffset: XYCoord, item: SpelDndContext) => { + const editor = getEditor(); + if (!editor) return; + + const coords = editor.renderer.pixelToScreenCoordinates(clientOffset.x, clientOffset.y); + editor.session.insert(coords, getText(item)); + }, + [getEditor, getText], + ); + + const setAcceptedLang = useContext(AcceptedLangCtx); + const [{ isOver }, connectDropTarget] = useDrop(() => ({ + accept: [DndTypes.VALUE], + hover: (item, monitor) => { + if (!monitor.canDrop()) return; + addPlaceholder(monitor.getClientOffset(), item); + }, + drop: (item, monitor) => { + if (!monitor.canDrop()) return; + insertValue(monitor.getClientOffset(), item); + }, + collect: (monitor) => ({ + isOver: monitor.isOver(), + }), + })); + + useEffect(() => { + if (!isOver) { + return removePlaceholder(); + } + setAcceptedLang?.(language); + }, [removePlaceholder, isOver, setAcceptedLang, language]); + + const { shouldBlockEditor } = useDragLayer((monitor) => ({ + shouldBlockEditor: monitor.isDragging() && monitor.getItemType() === DndTypes.VALUE, + })); + + useEffect(() => { + // cleanest way to block default drop behavior + getEditor()?.setOptions({ readOnly: shouldBlockEditor }); + }, [getEditor, shouldBlockEditor]); + + useEffect(() => { + const container = getEditor()?.container; + if (!container) return; + connectDropTarget(container); + }, [connectDropTarget, getEditor]); + + return { isOver }; +} diff --git a/designer/client/src/components/graph/node-modal/io/ContextTree.tsx b/designer/client/src/components/graph/node-modal/io/ContextTree.tsx index f9557138c6d..882375dad9c 100644 --- a/designer/client/src/components/graph/node-modal/io/ContextTree.tsx +++ b/designer/client/src/components/graph/node-modal/io/ContextTree.tsx @@ -1,57 +1,178 @@ -import { Box, styled } from "@mui/material"; -import { mapValues } from "lodash"; -import React from "react"; -import type { InspectorNodeParams } from "react-inspector"; -import Inspector, { chromeDark, ObjectLabel, ObjectName } from "react-inspector"; +import { Box } from "@mui/material"; +import { get, mapValues } from "lodash"; +import type { PropsWithChildren } from "react"; +import React, { useCallback, useEffect, useMemo } from "react"; +import type { Styling } from "react-base16-styling/src/types"; +import { useDrag } from "react-dnd"; +import { getEmptyImage } from "react-dnd-html5-backend"; +import type { KeyPath } from "react-json-tree"; +import { JSONTree } from "react-json-tree"; import type { ResultContextJson } from "../../../../http/resultsWithCountsDto"; +import { DndTypes } from "../../../DndTypes"; -export function ContextTree({ context, oldFields = [] }: { context: ResultContextJson; oldFields?: string[] }): JSX.Element { - const data = mapValues(context?.variables, (v) => v?.pretty); - const keys = Object.keys(data); - const expandedFields = keys.filter((k) => !oldFields.includes(k) || (k !== "inputMeta" && keys.length === oldFields.length)); +function generateTransparentPngDataURL() { + const canvas = document.createElement("canvas"); + canvas.width = 1; + canvas.height = 1; + + const ctx = canvas.getContext("2d"); + ctx.clearRect(0, 0, 1, 1); + + return canvas.toDataURL("image/png"); +} + +const emptyImage = generateTransparentPngDataURL(); +const getEmptyDragImage = () => { + const dragImage = document.createElement("img"); + dragImage.classList.add( + css({ + position: "absolute", + top: -1000, + left: -1000, + }), + ); + dragImage.src = emptyImage; + return dragImage; +}; + +class NotChanged { + constructor(private value) {} + get [Symbol.toStringTag]() { + return "same as input"; + } +} + +function normalizePath(keyPath: KeyPath | (string | number)[]) { + return [...keyPath].reverse(); +} + +export type SpelDndContext = { + value: any; + path: KeyPath; + type: "key" | "value"; +}; + +function DraggableValue({ + disabled, + path, + value, + type = "key", + children, +}: PropsWithChildren<{ + disabled?: boolean; + path: SpelDndContext["path"]; + value: unknown; + type?: SpelDndContext["type"]; +}>) { + const [{ isActive }, drag, preview] = useDrag(() => ({ + type: DndTypes.VALUE, + item: { path, type, value }, + options: { dropEffect: "copy" }, + canDrag: !disabled, + collect: (monitor) => ({ isActive: monitor.isDragging() }), + })); + + useEffect(() => { + preview(getEmptyImage()); + return () => { + preview(null); + }; + }, [preview]); + + console.log(isActive); return ( ({ - "--objectNameColor": theme.palette.primary.main, - zoom: 1.5, - background: "rgba(0,0,0,0.5)", - "&> ol > li": { - "&> div:first-of-type": { - display: "none", - }, - "&> ol:first-of-type": { - paddingLeft: "6px !important", - }, + component="span" + ref={drag} + sx={{ + cursor: disabled ? "default" : "grab", + "&:active": { + cursor: "grabbing", }, - })} + }} > - `$.${k}`)]} - data={data} - sortObjectKeys - nodeRenderer={getNodeRenderer(oldFields)} - /> + {children} ); } -const ValueWrapper = styled("span")({ - "--objectNameColor": "lime", -}); - -const getNodeRenderer = (oldFields: string[]) => { - return function renderer({ name, data, isNonenumerable, expanded, depth }: InspectorNodeParams) { - const Wrapper = depth !== 1 || oldFields.length < 1 || oldFields.includes(name) ? React.Fragment : ValueWrapper; - return ( - - {expanded ? : } - - ); - }; -}; +export function ContextTree({ context, oldFields = [] }: { context: ResultContextJson; oldFields?: string[] }): JSX.Element { + const data = useMemo(() => mapValues(context?.variables, (v) => v?.pretty), [context?.variables]); + const keys = useMemo(() => Object.keys(data), [data]); + + const expandedFields = useMemo( + () => keys.filter((key) => !oldFields.includes(key) || (key !== "inputMeta" && keys.length === oldFields.length)), + [keys, oldFields], + ); + + const isOldField = useCallback( + (keyPath: KeyPath) => keyPath.length === 1 && oldFields.length >= 1 && oldFields.includes(keyPath[0].toString()), + [oldFields], + ); + + return ( + ({ + style: { + ...style, + opacity: isOldField(keyPath) ? 0.5 : null, + }, + }), + value: ({ style }: Styling, nodeType: string, keyPath: string[]) => ({ + style: { + ...style, + opacity: isOldField(keyPath) ? 0.5 : null, + }, + }), + }} + shouldExpandNodeInitially={([key], data, level) => { + return level < 2 && expandedFields.includes(key?.toString()); + }} + getItemString={(nodeType, data, itemType, itemString, keyPath) => { + const path = normalizePath(keyPath); + return ( + <> + + {nodeType === "Array" ? "List" : nodeType === "Object" ? "Record" : itemType} ({itemString}) + + + ); + }} + labelRenderer={(keyPath, nodeType, expanded, expandable) => { + const path = normalizePath(keyPath); + return ( + <> + + {keyPath[0]} + + : + + ); + }} + valueRenderer={(valueAsString: string, value, ...keyPath) => { + const path = normalizePath(keyPath); + return ( + + {valueAsString} + + ); + }} + postprocessValue={(value) => { + if (oldFields.map((f) => data[f]).includes(value)) { + return new NotChanged(value); + } + return value; + }} + /> + ); +} diff --git a/designer/client/src/components/graph/node-modal/io/InputOutputContent.tsx b/designer/client/src/components/graph/node-modal/io/InputOutputContent.tsx index ecce87ecf99..47773a6313f 100644 --- a/designer/client/src/components/graph/node-modal/io/InputOutputContent.tsx +++ b/designer/client/src/components/graph/node-modal/io/InputOutputContent.tsx @@ -2,6 +2,7 @@ import type { PropsOf } from "@emotion/react/dist/emotion-react.cjs"; import { Stack } from "@mui/material"; import React, { forwardRef } from "react"; +import { ValueDragPreview } from "../../../ValueDragPreview"; import { StyledContent } from "../node/StyledHeader"; import { InputOutputLayout } from "./InputOutputLayout"; @@ -10,7 +11,9 @@ export const InputOutputContent = forwardRef - + + +
diff --git a/designer/client/src/components/graph/node-modal/io/VariableContextTree.tsx b/designer/client/src/components/graph/node-modal/io/VariableContextTree.tsx index 6bb134da84e..b4573a0ea70 100644 --- a/designer/client/src/components/graph/node-modal/io/VariableContextTree.tsx +++ b/designer/client/src/components/graph/node-modal/io/VariableContextTree.tsx @@ -1,7 +1,7 @@ import { CloudOff } from "@mui/icons-material"; import { alpha, Box, Fade, Stack, Typography } from "@mui/material"; import type { MouseEvent } from "react"; -import React, { memo, useCallback, useEffect, useMemo, useState } from "react"; +import React, { useCallback, useEffect, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useDispatch, useSelector } from "react-redux"; @@ -187,7 +187,7 @@ export const VariableContextTree = memo(function ValuesContextTree({ showNodes={showNodes} > {direction === "output" ? <>{r.error} : null} - + ))} diff --git a/designer/client/src/containers/theme/styles.ts b/designer/client/src/containers/theme/styles.ts index 670a00f0bc3..74a2f8805b9 100644 --- a/designer/client/src/containers/theme/styles.ts +++ b/designer/client/src/containers/theme/styles.ts @@ -55,7 +55,12 @@ const aceEditorStyles = (theme: Theme) => ({ ".ace-nussknacker .ace_marker-layer .ace_step": { background: "rgb(102, 82, 0)", }, + ".ace-nussknacker .ace_marker-layer .ace_bracket": { + display: "none", + }, + ".ace-nussknacker.ace_focus .ace_marker-layer .ace_bracket": { + display: "block", margin: 0, border: "1px solid #FFC66D", }, diff --git a/designer/client/src/helpers/memoizeByArgsWithTTL.ts b/designer/client/src/helpers/memoizeByArgsWithTTL.ts index 6a0aa835c9f..7b1cb0fbee4 100644 --- a/designer/client/src/helpers/memoizeByArgsWithTTL.ts +++ b/designer/client/src/helpers/memoizeByArgsWithTTL.ts @@ -2,8 +2,8 @@ import { memoize } from "lodash"; import { CacheWithTTL } from "./CacheWithTTL"; -export const memoizeByArgsWithTTL = any>(func: T) => { +export const memoizeByArgsWithTTL = any>(func: T, ttl?: number, maxSize?: number) => { const memoized = memoize(func, (...args) => args); - memoized.cache = new CacheWithTTL, ReturnType>(); + memoized.cache = new CacheWithTTL, ReturnType>(ttl, maxSize); return memoized; }; From 8582911c43fabb03c8c047bb710e7e1583f5964e Mon Sep 17 00:00:00 2001 From: Julian Wielga Date: Thu, 26 Jun 2025 19:14:19 +0200 Subject: [PATCH 06/10] cleanup & fixes --- .../src/components/ComponentDragPreview.tsx | 3 ++- .../src/components/ValueDragPreview.tsx | 2 +- .../editors/expression/AceWithSettings.tsx | 2 +- .../editors/expression/AceWrapper.tsx | 3 ++- .../graph/node-modal/io/ContextTree.tsx | 25 ------------------- .../node-modal/io/VariableContextTree.tsx | 2 +- 6 files changed, 7 insertions(+), 30 deletions(-) diff --git a/designer/client/src/components/ComponentDragPreview.tsx b/designer/client/src/components/ComponentDragPreview.tsx index e7b072f3d2e..5b061385ebc 100644 --- a/designer/client/src/components/ComponentDragPreview.tsx +++ b/designer/client/src/components/ComponentDragPreview.tsx @@ -1,4 +1,5 @@ -import React, { forwardRef, useEffect, useMemo } from "react"; +import { css } from "@emotion/css"; +import React, { forwardRef, useEffect, useMemo, useState } from "react"; import { useDragDropManager, useDragLayer } from "react-dnd"; import { createPortal } from "react-dom"; import { useDebouncedValue } from "rooks"; diff --git a/designer/client/src/components/ValueDragPreview.tsx b/designer/client/src/components/ValueDragPreview.tsx index dce41e81ff2..2fa15e9898a 100644 --- a/designer/client/src/components/ValueDragPreview.tsx +++ b/designer/client/src/components/ValueDragPreview.tsx @@ -1,6 +1,6 @@ import { Box, Paper, Typography } from "@mui/material"; import type { Dispatch, PropsWithChildren, SetStateAction } from "react"; -import React, { createContext, forwardRef, useMemo } from "react"; +import React, { createContext, forwardRef, useMemo, useState } from "react"; import { useDragDropManager, useDragLayer } from "react-dnd"; import { createPortal } from "react-dom"; diff --git a/designer/client/src/components/graph/node-modal/editors/expression/AceWithSettings.tsx b/designer/client/src/components/graph/node-modal/editors/expression/AceWithSettings.tsx index f339d0e2063..146931af913 100644 --- a/designer/client/src/components/graph/node-modal/editors/expression/AceWithSettings.tsx +++ b/designer/client/src/components/graph/node-modal/editors/expression/AceWithSettings.tsx @@ -2,7 +2,7 @@ import { GlobalStyles } from "@mui/material"; import { throttle } from "lodash"; import type { ForwardedRef } from "react"; -import React, { forwardRef, useEffect, useMemo } from "react"; +import React, { forwardRef, useEffect, useMemo, useRef } from "react"; import type ReactAce from "react-ace/lib/ace"; import { useMergeRefs } from "rooks"; diff --git a/designer/client/src/components/graph/node-modal/editors/expression/AceWrapper.tsx b/designer/client/src/components/graph/node-modal/editors/expression/AceWrapper.tsx index 68dc5391879..2cd5edb6172 100644 --- a/designer/client/src/components/graph/node-modal/editors/expression/AceWrapper.tsx +++ b/designer/client/src/components/graph/node-modal/editors/expression/AceWrapper.tsx @@ -5,7 +5,8 @@ import type { Ace } from "ace-builds"; import { trimStart } from "lodash"; import type { ForwardedRef, ReactNode } from "react"; import React, { forwardRef, useMemo } from "react"; -import type ReactAce, { IAceEditorProps } from "react-ace/lib/ace"; +import type ReactAce from "react-ace/lib/ace"; +import type { IAceEditorProps } from "react-ace/lib/ace"; import type { ICommand } from "react-ace/lib/types"; import type { IAceOptions, IEditorProps } from "react-ace/src/types"; diff --git a/designer/client/src/components/graph/node-modal/io/ContextTree.tsx b/designer/client/src/components/graph/node-modal/io/ContextTree.tsx index 882375dad9c..b95cff9992b 100644 --- a/designer/client/src/components/graph/node-modal/io/ContextTree.tsx +++ b/designer/client/src/components/graph/node-modal/io/ContextTree.tsx @@ -11,31 +11,6 @@ import { JSONTree } from "react-json-tree"; import type { ResultContextJson } from "../../../../http/resultsWithCountsDto"; import { DndTypes } from "../../../DndTypes"; -function generateTransparentPngDataURL() { - const canvas = document.createElement("canvas"); - canvas.width = 1; - canvas.height = 1; - - const ctx = canvas.getContext("2d"); - ctx.clearRect(0, 0, 1, 1); - - return canvas.toDataURL("image/png"); -} - -const emptyImage = generateTransparentPngDataURL(); -const getEmptyDragImage = () => { - const dragImage = document.createElement("img"); - dragImage.classList.add( - css({ - position: "absolute", - top: -1000, - left: -1000, - }), - ); - dragImage.src = emptyImage; - return dragImage; -}; - class NotChanged { constructor(private value) {} get [Symbol.toStringTag]() { diff --git a/designer/client/src/components/graph/node-modal/io/VariableContextTree.tsx b/designer/client/src/components/graph/node-modal/io/VariableContextTree.tsx index b4573a0ea70..1f7b35811ca 100644 --- a/designer/client/src/components/graph/node-modal/io/VariableContextTree.tsx +++ b/designer/client/src/components/graph/node-modal/io/VariableContextTree.tsx @@ -1,7 +1,7 @@ import { CloudOff } from "@mui/icons-material"; import { alpha, Box, Fade, Stack, Typography } from "@mui/material"; import type { MouseEvent } from "react"; -import React, { useCallback, useEffect, useMemo } from "react"; +import React, { useCallback, useEffect, useMemo, useState, memo } from "react"; import { useTranslation } from "react-i18next"; import { useDispatch, useSelector } from "react-redux"; From 81122e6d88211fec9546a7177d443d4512cdf1f2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 26 Jun 2025 20:21:00 +0200 Subject: [PATCH 07/10] Updated snapshots (#8280) Co-authored-by: JulianWielga <965924+JulianWielga@users.noreply.github.com> --- ...r should display colorfull sql code #0.png | Bin 18457 -> 18110 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/designer/client/cypress/e2e/__image_snapshots__/electron/Linux/Sql editor should display colorfull sql code #0.png b/designer/client/cypress/e2e/__image_snapshots__/electron/Linux/Sql editor should display colorfull sql code #0.png index f7328c0139de57613e8a5cc16ff1c978428dff4e..689ab905fd55f9f7c432b50fc8bc60874a31296b 100644 GIT binary patch delta 4513 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 delta 4965 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 So-a>AykMI88r5pHsQ&_(51CT{ From e75410b629a2b58aa74c2f24afef05bfac473947 Mon Sep 17 00:00:00 2001 From: Julian Wielga Date: Thu, 26 Jun 2025 20:19:29 +0200 Subject: [PATCH 08/10] cleanup & fixes --- .../src/components/ComponentDragPreview.tsx | 1 - .../client/test/Editors/RawEditor-test.tsx | 34 ++++++++++-------- .../test/Editors/SpelTemplateEditor-test.tsx | 24 +++++++------ .../client/test/Editors/SqlEditor-test.tsx | 36 ++++++++++--------- 4 files changed, 53 insertions(+), 42 deletions(-) diff --git a/designer/client/src/components/ComponentDragPreview.tsx b/designer/client/src/components/ComponentDragPreview.tsx index 5b061385ebc..29bcfa3f843 100644 --- a/designer/client/src/components/ComponentDragPreview.tsx +++ b/designer/client/src/components/ComponentDragPreview.tsx @@ -19,7 +19,6 @@ export function useNotNull(value: T) { }, [value]); return current; } - function PreviewElement(props: ComponentPreviewProps) { if (props.node.type === StickyNoteType) { return ; diff --git a/designer/client/test/Editors/RawEditor-test.tsx b/designer/client/test/Editors/RawEditor-test.tsx index 7b0db737a68..1dbb42a73a6 100644 --- a/designer/client/test/Editors/RawEditor-test.tsx +++ b/designer/client/test/Editors/RawEditor-test.tsx @@ -1,12 +1,14 @@ -import * as React from "react"; - import { render, screen } from "@testing-library/react"; -import { SpelEditor } from "../../src/components/graph/node-modal/editors/expression/SpelEditor"; +import { HTML5toTouch } from "rdndmb-html5-to-touch"; +import * as React from "react"; +import { DndProvider } from "react-dnd-multi-backend"; import { Provider } from "react-redux"; import configureMockStore from "redux-mock-store/lib"; -import { mockFieldErrors, mockValueChange } from "./helpers"; -import { NuThemeProvider } from "../../src/containers/theme/nuThemeProvider"; + +import { SpelEditor } from "../../src/components/graph/node-modal/editors/expression/SpelEditor"; import { nodeInputWithError } from "../../src/components/graph/node-modal/NodeDetailsContent/NodeTableStyled"; +import { NuThemeProvider } from "../../src/containers/theme/nuThemeProvider"; +import { mockFieldErrors, mockValueChange } from "./helpers"; const mockStore = configureMockStore(); @@ -29,16 +31,18 @@ describe("SpelEditor", () => { const { container } = render( - + + + , ); diff --git a/designer/client/test/Editors/SpelTemplateEditor-test.tsx b/designer/client/test/Editors/SpelTemplateEditor-test.tsx index 198d6dc291c..d8866f3e783 100644 --- a/designer/client/test/Editors/SpelTemplateEditor-test.tsx +++ b/designer/client/test/Editors/SpelTemplateEditor-test.tsx @@ -1,8 +1,10 @@ +import { HTML5toTouch } from "rdndmb-html5-to-touch"; import * as React from "react"; import "ace-builds/src-noconflict/ace"; import { render, screen } from "@testing-library/react"; import "ace-builds/src-noconflict/ext-language_tools"; +import { DndProvider } from "react-dnd-multi-backend"; import { Provider } from "react-redux"; import configureMockStore from "redux-mock-store/lib"; import { SpelTemplateEditor } from "../../src/components/graph/node-modal/editors/expression/SpelTemplateEditor"; @@ -30,16 +32,18 @@ describe("SpelTemplateEditor", () => { render( - + + + , ); diff --git a/designer/client/test/Editors/SqlEditor-test.tsx b/designer/client/test/Editors/SqlEditor-test.tsx index a446f5f8b34..e08580352c1 100644 --- a/designer/client/test/Editors/SqlEditor-test.tsx +++ b/designer/client/test/Editors/SqlEditor-test.tsx @@ -1,13 +1,15 @@ -import * as React from "react"; -import "ace-builds/src-noconflict/ace"; - import { render, screen } from "@testing-library/react"; import "ace-builds/src-noconflict/ext-language_tools"; -import { SqlEditor } from "../../src/components/graph/node-modal/editors/expression/SqlEditor"; +import { HTML5toTouch } from "rdndmb-html5-to-touch"; +import * as React from "react"; +import "ace-builds/src-noconflict/ace"; +import { DndProvider } from "react-dnd-multi-backend"; import { Provider } from "react-redux"; import configureMockStore from "redux-mock-store/lib"; -import { mockFieldErrors, mockFormatter, mockValueChange } from "./helpers"; + +import { SqlEditor } from "../../src/components/graph/node-modal/editors/expression/SqlEditor"; import { NuThemeProvider } from "../../src/containers/theme/nuThemeProvider"; +import { mockFieldErrors, mockFormatter, mockValueChange } from "./helpers"; const mockStore = configureMockStore(); @@ -30,17 +32,19 @@ describe("SqlEditor", () => { render( - + + + , ); From c4444e073cdcbc0867fa2d42f83910fe1f508e00 Mon Sep 17 00:00:00 2001 From: Julian Wielga Date: Thu, 26 Jun 2025 21:00:59 +0200 Subject: [PATCH 09/10] cleanup & fixes --- designer/client/test/Editors/SpelTemplateEditor-test.tsx | 2 +- designer/client/test/Editors/SqlEditor-test.tsx | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/designer/client/test/Editors/SpelTemplateEditor-test.tsx b/designer/client/test/Editors/SpelTemplateEditor-test.tsx index d8866f3e783..3df1b2e5e47 100644 --- a/designer/client/test/Editors/SpelTemplateEditor-test.tsx +++ b/designer/client/test/Editors/SpelTemplateEditor-test.tsx @@ -1,12 +1,12 @@ import { HTML5toTouch } from "rdndmb-html5-to-touch"; import * as React from "react"; import "ace-builds/src-noconflict/ace"; - import { render, screen } from "@testing-library/react"; import "ace-builds/src-noconflict/ext-language_tools"; import { DndProvider } from "react-dnd-multi-backend"; import { Provider } from "react-redux"; import configureMockStore from "redux-mock-store/lib"; + import { SpelTemplateEditor } from "../../src/components/graph/node-modal/editors/expression/SpelTemplateEditor"; import { mockFieldErrors, mockValueChange } from "./helpers"; import { NuThemeProvider } from "../../src/containers/theme/nuThemeProvider"; diff --git a/designer/client/test/Editors/SqlEditor-test.tsx b/designer/client/test/Editors/SqlEditor-test.tsx index e08580352c1..120a1777e73 100644 --- a/designer/client/test/Editors/SqlEditor-test.tsx +++ b/designer/client/test/Editors/SqlEditor-test.tsx @@ -1,15 +1,15 @@ -import { render, screen } from "@testing-library/react"; -import "ace-builds/src-noconflict/ext-language_tools"; -import { HTML5toTouch } from "rdndmb-html5-to-touch"; import * as React from "react"; import "ace-builds/src-noconflict/ace"; +import "ace-builds/src-noconflict/ext-language_tools"; +import { render, screen } from "@testing-library/react"; +import { HTML5toTouch } from "rdndmb-html5-to-touch"; import { DndProvider } from "react-dnd-multi-backend"; import { Provider } from "react-redux"; import configureMockStore from "redux-mock-store/lib"; import { SqlEditor } from "../../src/components/graph/node-modal/editors/expression/SqlEditor"; -import { NuThemeProvider } from "../../src/containers/theme/nuThemeProvider"; import { mockFieldErrors, mockFormatter, mockValueChange } from "./helpers"; +import { NuThemeProvider } from "../../src/containers/theme/nuThemeProvider"; const mockStore = configureMockStore(); From 7b86be45835b23f19ca2d516b1c7d9548b305c79 Mon Sep 17 00:00:00 2001 From: Julian Wielga Date: Fri, 27 Jun 2025 18:23:31 +0200 Subject: [PATCH 10/10] cleanup and bugfixes --- designer/client/src/actions/nk/liveData.ts | 4 ++-- .../graph/node-modal/io/ContextTree.tsx | 1 - .../graph/node-modal/io/InputOutputContext.tsx | 2 +- .../liveData/LiveDataThroughputs.tsx | 18 +++++++++++------- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/designer/client/src/actions/nk/liveData.ts b/designer/client/src/actions/nk/liveData.ts index 4ac18f0015a..2365eb350c2 100644 --- a/designer/client/src/actions/nk/liveData.ts +++ b/designer/client/src/actions/nk/liveData.ts @@ -28,7 +28,7 @@ const REFRESH_TIME = 1000; let intervalId: number; -function fetchAndDisplayLiveData(showErrors = false): ThunkAction { +function fetchAndDisplayLiveData(showErrors = false, refresh = REFRESH_TIME): ThunkAction { return (dispatch, getState) => { async function perform(showErrors = false) { dispatch({ type: "FETCH_LIVE_DATA" }); @@ -57,7 +57,7 @@ function fetchAndDisplayLiveData(showErrors = false): ThunkAction { if (!intervalId) { dispatch({ type: "LIVE_DATA_STARTED" }); - intervalId = window.setInterval(perform, REFRESH_TIME); + intervalId = window.setInterval(perform, refresh); } }; } diff --git a/designer/client/src/components/graph/node-modal/io/ContextTree.tsx b/designer/client/src/components/graph/node-modal/io/ContextTree.tsx index b95cff9992b..e6fa99cd801 100644 --- a/designer/client/src/components/graph/node-modal/io/ContextTree.tsx +++ b/designer/client/src/components/graph/node-modal/io/ContextTree.tsx @@ -55,7 +55,6 @@ function DraggableValue({ }; }, [preview]); - console.log(isActive); return ( ({ id, - ...transitionResults?.find((r) => r.destinationNodeId === id), + ...transitionResults?.find((r) => r.destinationNodeId == id), })); }, [nodeId, nodeTransitionResults, scenario]); diff --git a/designer/client/src/containers/liveData/LiveDataThroughputs.tsx b/designer/client/src/containers/liveData/LiveDataThroughputs.tsx index d94046696b2..51ad884e026 100644 --- a/designer/client/src/containers/liveData/LiveDataThroughputs.tsx +++ b/designer/client/src/containers/liveData/LiveDataThroughputs.tsx @@ -4,6 +4,7 @@ import { useSelector } from "react-redux"; import { useUserSettings } from "../../common/userSettings"; import { useGraph } from "../../components/graph/GraphContext"; +import type { NodeTransitionResult } from "../../http/resultsWithCountsDto"; import { getIsLiveDataWorking, getLiveDataLastUpdate, @@ -74,16 +75,19 @@ export function LiveDataThroughputs() { const graphInstance = graphGetter(); graphInstance?.graph.getElements().forEach((model) => { - const events = newEvents.filter((e) => e.sourceNodeId === model.id); + const isMatchingModel = ({ + sourceNodeId, + destinationNodeId, + }: Pick): boolean => { + if (model.hasPort("In")) return model.id === destinationNodeId; + if (model.hasPort("Out")) return model.id === sourceNodeId; + }; + + const events = newEvents.filter(isMatchingModel); const el = graphInstance.processGraphPaper.findViewByModel(model)?.el; const nodeInputThroughput = enabled - ? transitionResults - .filter(({ sourceNodeId, destinationNodeId }) => { - if (model.hasPort("In")) return destinationNodeId === model.id; - if (model.hasPort("Out")) return sourceNodeId === model.id; - }) - .reduce((sum, { currentThroughput }) => sum + currentThroughput, 0) + ? transitionResults.filter(isMatchingModel).reduce((sum, { currentThroughput }) => sum + currentThroughput, 0) : 0; const animations = el.getAnimations().filter(({ id }) => id === "pulse2" || id === "pulse");