From b3c1b941b32014b40daf69b10bb8453c8a963a80 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Date: Fri, 19 Jul 2024 13:12:19 -0300 Subject: [PATCH] refactor: update template api, handleonnewvalue and handlenodeclass hooks (#2628) * Added Custom Component URL * Added Post Template Value mutation * Changed HandleOnNewValue hook to ParameterValue mutation * refactored some states * Added NumberInput component to replace input type=number. Added logic to maintain cursor at the same place when editing * Refactored post-template-value to remove all logic * Removed hooks from custom hook and removed mutation definition from parametercomponent * Added mutate-template helper to call debounced mutation * Changed handle new value to use the created function and update as requested. * Removed pDebounce from imports * Refactored FetchDataOnMount to only call mutateTemplate * Refactored ParameterComponent to use the new MutateTemplate and the loading from the mutation * removed handle refresh button * Changed handleOnNewValue to change the value of any parameter of a template field * Changed hooks to receive node instead of data, added HandleOnNewValue hook on tableNodeCellRender * added SetNodeClass to update internal table state of EditNodeModal * Removed other handle new value and node class, updated every component to use the same one * Updated parameter component to use the same nodeclass hook * FIxed hook to only assign value if code is present * Fixed type attribution on useHandleNodeClass --- src/frontend/package-lock.json | 602 +++++++++++++++++- src/frontend/package.json | 1 + .../components/parameterComponent/index.tsx | 75 ++- .../CustomNodes/helpers/mutate-template.ts | 44 ++ .../hooks/use-fetch-data-on-mount.tsx | 63 +- .../hooks/use-handle-new-value.tsx | 138 ++-- .../hooks/use-handle-node-class.tsx | 22 +- .../hooks/use-handle-refresh-buttons.tsx | 47 -- .../src/components/intComponent/index.tsx | 68 +- .../tableNodeCellRender/cellTypeStr.tsx | 2 +- .../components/tableNodeCellRender/index.tsx | 52 +- .../src/controllers/API/helpers/constants.ts | 1 + .../queries/nodes/use-post-template-value.ts | 61 ++ .../editNodeModal/hooks/use-column-defs.tsx | 7 +- .../hooks/use-handle-new-value.tsx | 84 --- .../hooks/use-handle-node-class.tsx | 39 -- .../src/modals/editNodeModal/index.tsx | 20 +- .../components/nodeToolbarComponent/index.tsx | 69 +- src/frontend/src/types/components/index.ts | 6 +- 19 files changed, 965 insertions(+), 436 deletions(-) create mode 100644 src/frontend/src/CustomNodes/helpers/mutate-template.ts delete mode 100644 src/frontend/src/CustomNodes/hooks/use-handle-refresh-buttons.tsx create mode 100644 src/frontend/src/controllers/API/queries/nodes/use-post-template-value.ts delete mode 100644 src/frontend/src/modals/editNodeModal/hooks/use-handle-new-value.tsx delete mode 100644 src/frontend/src/modals/editNodeModal/hooks/use-handle-node-class.tsx diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index ed2b933c3..f23b03c30 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "langflow", "version": "0.1.2", "dependencies": { + "@chakra-ui/number-input": "^2.1.2", "@headlessui/react": "^2.0.4", "@hookform/resolvers": "^3.6.0", "@million/lint": "^1.0.0-rc.26", @@ -764,6 +765,293 @@ "node": ">=6.9.0" } }, + "node_modules/@chakra-ui/anatomy": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/anatomy/-/anatomy-2.2.2.tgz", + "integrity": "sha512-MV6D4VLRIHr4PkW4zMyqfrNS1mPlCTiCXwvYGtDFQYr+xHFfonhAuf9WjsSc0nyp2m0OdkSLnzmVKkZFLo25Tg==", + "license": "MIT", + "peer": true + }, + "node_modules/@chakra-ui/color-mode": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/color-mode/-/color-mode-2.2.0.tgz", + "integrity": "sha512-niTEA8PALtMWRI9wJ4LL0CSBDo8NBfLNp4GD6/0hstcm3IlbBHTVKxN6HwSaoNYfphDQLxCjT4yG+0BJA5tFpg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@chakra-ui/react-use-safe-layout-effect": "2.1.0" + }, + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/counter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/counter/-/counter-2.1.0.tgz", + "integrity": "sha512-s6hZAEcWT5zzjNz2JIWUBzRubo9la/oof1W7EKZVVfPYHERnl5e16FmBC79Yfq8p09LQ+aqFKm/etYoJMMgghw==", + "license": "MIT", + "dependencies": { + "@chakra-ui/number-utils": "2.0.7", + "@chakra-ui/react-use-callback-ref": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" + }, + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/form-control": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/form-control/-/form-control-2.2.0.tgz", + "integrity": "sha512-wehLC1t4fafCVJ2RvJQT2jyqsAwX7KymmiGqBu7nQoQz8ApTkGABWpo/QwDh3F/dBLrouHDoOvGmYTqft3Mirw==", + "license": "MIT", + "dependencies": { + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" + }, + "peerDependencies": { + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" + } + }, + "node_modules/@chakra-ui/icon": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/icon/-/icon-3.2.0.tgz", + "integrity": "sha512-xxjGLvlX2Ys4H0iHrI16t74rG9EBcpFvJ3Y3B7KMQTrnW34Kf7Da/UC8J67Gtx85mTHW020ml85SVPKORWNNKQ==", + "license": "MIT", + "dependencies": { + "@chakra-ui/shared-utils": "2.0.5" + }, + "peerDependencies": { + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" + } + }, + "node_modules/@chakra-ui/number-input": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/number-input/-/number-input-2.1.2.tgz", + "integrity": "sha512-pfOdX02sqUN0qC2ysuvgVDiws7xZ20XDIlcNhva55Jgm095xjm8eVdIBfNm3SFbSUNxyXvLTW/YQanX74tKmuA==", + "license": "MIT", + "dependencies": { + "@chakra-ui/counter": "2.1.0", + "@chakra-ui/form-control": "2.2.0", + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-callback-ref": "2.1.0", + "@chakra-ui/react-use-event-listener": "2.1.0", + "@chakra-ui/react-use-interval": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/react-use-safe-layout-effect": "2.1.0", + "@chakra-ui/react-use-update-effect": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" + }, + "peerDependencies": { + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" + } + }, + "node_modules/@chakra-ui/number-utils": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@chakra-ui/number-utils/-/number-utils-2.0.7.tgz", + "integrity": "sha512-yOGxBjXNvLTBvQyhMDqGU0Oj26s91mbAlqKHiuw737AXHt0aPllOthVUqQMeaYLwLCjGMg0jtI7JReRzyi94Dg==", + "license": "MIT" + }, + "node_modules/@chakra-ui/object-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/object-utils/-/object-utils-2.1.0.tgz", + "integrity": "sha512-tgIZOgLHaoti5PYGPTwK3t/cqtcycW0owaiOXoZOcpwwX/vlVb+H1jFsQyWiiwQVPt9RkoSLtxzXamx+aHH+bQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@chakra-ui/react-context": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-context/-/react-context-2.1.0.tgz", + "integrity": "sha512-iahyStvzQ4AOwKwdPReLGfDesGG+vWJfEsn0X/NoGph/SkN+HXtv2sCfYFFR9k7bb+Kvc6YfpLlSuLvKMHi2+w==", + "license": "MIT", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-types/-/react-types-2.0.7.tgz", + "integrity": "sha512-12zv2qIZ8EHwiytggtGvo4iLT0APris7T0qaAWqzpUGS0cdUtR8W+V1BJ5Ocq+7tA6dzQ/7+w5hmXih61TuhWQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-use-callback-ref": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-callback-ref/-/react-use-callback-ref-2.1.0.tgz", + "integrity": "sha512-efnJrBtGDa4YaxDzDE90EnKD3Vkh5a1t3w7PhnRQmsphLy3g2UieasoKTlT2Hn118TwDjIv5ZjHJW6HbzXA9wQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-use-event-listener": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-event-listener/-/react-use-event-listener-2.1.0.tgz", + "integrity": "sha512-U5greryDLS8ISP69DKDsYcsXRtAdnTQT+jjIlRYZ49K/XhUR/AqVZCK5BkR1spTDmO9H8SPhgeNKI70ODuDU/Q==", + "license": "MIT", + "dependencies": { + "@chakra-ui/react-use-callback-ref": "2.1.0" + }, + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-use-interval": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-interval/-/react-use-interval-2.1.0.tgz", + "integrity": "sha512-8iWj+I/+A0J08pgEXP1J1flcvhLBHkk0ln7ZvGIyXiEyM6XagOTJpwNhiu+Bmk59t3HoV/VyvyJTa+44sEApuw==", + "license": "MIT", + "dependencies": { + "@chakra-ui/react-use-callback-ref": "2.1.0" + }, + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-use-merge-refs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-merge-refs/-/react-use-merge-refs-2.1.0.tgz", + "integrity": "sha512-lERa6AWF1cjEtWSGjxWTaSMvneccnAVH4V4ozh8SYiN9fSPZLlSG3kNxfNzdFvMEhM7dnP60vynF7WjGdTgQbQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-use-safe-layout-effect": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-safe-layout-effect/-/react-use-safe-layout-effect-2.1.0.tgz", + "integrity": "sha512-Knbrrx/bcPwVS1TorFdzrK/zWA8yuU/eaXDkNj24IrKoRlQrSBFarcgAEzlCHtzuhufP3OULPkELTzz91b0tCw==", + "license": "MIT", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-use-update-effect": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-update-effect/-/react-use-update-effect-2.1.0.tgz", + "integrity": "sha512-ND4Q23tETaR2Qd3zwCKYOOS1dfssojPLJMLvUtUbW5M9uW1ejYWgGUobeAiOVfSplownG8QYMmHTP86p/v0lbA==", + "license": "MIT", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-utils": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-utils/-/react-utils-2.0.12.tgz", + "integrity": "sha512-GbSfVb283+YA3kA8w8xWmzbjNWk14uhNpntnipHCftBibl0lxtQ9YqMFQLwuFOO0U2gYVocszqqDWX+XNKq9hw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@chakra-ui/utils": "2.0.15" + }, + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/shared-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@chakra-ui/shared-utils/-/shared-utils-2.0.5.tgz", + "integrity": "sha512-4/Wur0FqDov7Y0nCXl7HbHzCg4aq86h+SXdoUeuCMD3dSj7dpsVnStLYhng1vxvlbUnLpdF4oz5Myt3i/a7N3Q==", + "license": "MIT" + }, + "node_modules/@chakra-ui/styled-system": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/styled-system/-/styled-system-2.9.2.tgz", + "integrity": "sha512-To/Z92oHpIE+4nk11uVMWqo2GGRS86coeMmjxtpnErmWRdLcp1WVCVRAvn+ZwpLiNR+reWFr2FFqJRsREuZdAg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@chakra-ui/shared-utils": "2.0.5", + "csstype": "^3.1.2", + "lodash.mergewith": "4.6.2" + } + }, + "node_modules/@chakra-ui/system": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/system/-/system-2.6.2.tgz", + "integrity": "sha512-EGtpoEjLrUu4W1fHD+a62XR+hzC5YfsWm+6lO0Kybcga3yYEij9beegO0jZgug27V+Rf7vns95VPVP6mFd/DEQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@chakra-ui/color-mode": "2.2.0", + "@chakra-ui/object-utils": "2.1.0", + "@chakra-ui/react-utils": "2.0.12", + "@chakra-ui/styled-system": "2.9.2", + "@chakra-ui/theme-utils": "2.0.21", + "@chakra-ui/utils": "2.0.15", + "react-fast-compare": "3.2.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0", + "@emotion/styled": "^11.0.0", + "react": ">=18" + } + }, + "node_modules/@chakra-ui/theme": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/theme/-/theme-3.3.1.tgz", + "integrity": "sha512-Hft/VaT8GYnItGCBbgWd75ICrIrIFrR7lVOhV/dQnqtfGqsVDlrztbSErvMkoPKt0UgAkd9/o44jmZ6X4U2nZQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@chakra-ui/anatomy": "2.2.2", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/theme-tools": "2.1.2" + }, + "peerDependencies": { + "@chakra-ui/styled-system": ">=2.8.0" + } + }, + "node_modules/@chakra-ui/theme-tools": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/theme-tools/-/theme-tools-2.1.2.tgz", + "integrity": "sha512-Qdj8ajF9kxY4gLrq7gA+Azp8CtFHGO9tWMN2wfF9aQNgG9AuMhPrUzMq9AMQ0MXiYcgNq/FD3eegB43nHVmXVA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@chakra-ui/anatomy": "2.2.2", + "@chakra-ui/shared-utils": "2.0.5", + "color2k": "^2.0.2" + }, + "peerDependencies": { + "@chakra-ui/styled-system": ">=2.0.0" + } + }, + "node_modules/@chakra-ui/theme-utils": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/@chakra-ui/theme-utils/-/theme-utils-2.0.21.tgz", + "integrity": "sha512-FjH5LJbT794r0+VSCXB3lT4aubI24bLLRWB+CuRKHijRvsOg717bRdUN/N1fEmEpFnRVrbewttWh/OQs0EWpWw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/styled-system": "2.9.2", + "@chakra-ui/theme": "3.3.1", + "lodash.mergewith": "4.6.2" + } + }, + "node_modules/@chakra-ui/utils": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.15.tgz", + "integrity": "sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/lodash.mergewith": "4.6.7", + "css-box-model": "1.2.1", + "framesync": "6.1.2", + "lodash.mergewith": "4.6.2" + } + }, "node_modules/@clack/core": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/@clack/core/-/core-0.3.4.tgz", @@ -798,6 +1086,195 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/react": { + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", + "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", + "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/styled": { + "version": "11.11.5", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz", + "integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/is-prop-valid": "^1.2.2", + "@emotion/serialize": "^1.1.4", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==", + "license": "MIT", + "peer": true + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -4997,8 +5474,17 @@ "node_modules/@types/lodash": { "version": "4.17.5", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.5.tgz", - "integrity": "sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==", - "dev": true + "integrity": "sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==" + }, + "node_modules/@types/lodash.mergewith": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz", + "integrity": "sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/lodash": "*" + } }, "node_modules/@types/mathjax": { "version": "0.0.37", @@ -5027,6 +5513,13 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT", + "peer": true + }, "node_modules/@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", @@ -5388,6 +5881,49 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/babel-plugin-macros/node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/babel-plugin-macros/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/babel-plugin-syntax-hermes-parser": { "version": "0.21.1", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.21.1.tgz", @@ -6344,6 +6880,13 @@ "color-support": "bin.js" } }, + "node_modules/color2k": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz", + "integrity": "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==", + "license": "MIT", + "peer": true + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -6447,6 +6990,16 @@ "node": ">= 8" } }, + "node_modules/css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "license": "MIT", + "peer": true, + "dependencies": { + "tiny-invariant": "^1.0.6" + } + }, "node_modules/css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", @@ -7557,6 +8110,13 @@ "node": ">=8" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT", + "peer": true + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -7721,6 +8281,23 @@ } } }, + "node_modules/framesync": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz", + "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==", + "license": "MIT", + "peer": true, + "dependencies": { + "tslib": "2.4.0" + } + }, + "node_modules/framesync/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "license": "0BSD", + "peer": true + }, "node_modules/fs-extra": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", @@ -9226,6 +9803,13 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "license": "MIT", + "peer": true + }, "node_modules/lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", @@ -12205,6 +12789,13 @@ "react": ">=16.13.1" } }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT", + "peer": true + }, "node_modules/react-hook-form": { "version": "7.52.0", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.52.0.tgz", @@ -13614,6 +14205,13 @@ "inline-style-parser": "0.1.1" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT", + "peer": true + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", diff --git a/src/frontend/package.json b/src/frontend/package.json index 0b29f0c70..3da2e1c5f 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -3,6 +3,7 @@ "version": "0.1.2", "private": true, "dependencies": { + "@chakra-ui/number-input": "^2.1.2", "@headlessui/react": "^2.0.4", "@hookform/resolvers": "^3.6.0", "@million/lint": "^1.0.0-rc.26", diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index 7da923df6..e8eb15eef 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -1,3 +1,7 @@ +import { mutateTemplate } from "@/CustomNodes/helpers/mutate-template"; +import useHandleNodeClass from "@/CustomNodes/hooks/use-handle-node-class"; +import { usePostTemplateValue } from "@/controllers/API/queries/nodes/use-post-template-value"; +import useAlertStore from "@/stores/alertStore"; import { cloneDeep } from "lodash"; import { ReactNode, useEffect, useRef, useState } from "react"; import { useHotkeys } from "react-hotkeys-hook"; @@ -27,10 +31,6 @@ import { useShortcutsStore } from "../../../../stores/shortcuts"; import { useTypesStore } from "../../../../stores/typesStore"; import { APIClassType } from "../../../../types/api"; import { ParameterComponentType } from "../../../../types/components"; -import { - debouncedHandleUpdateValues, - handleUpdateValues, -} from "../../../../utils/parameterUtils"; import { convertObjToArray, convertValuesToNumbers, @@ -48,8 +48,6 @@ import { } from "../../../../utils/utils"; import useFetchDataOnMount from "../../../hooks/use-fetch-data-on-mount"; import useHandleOnNewValue from "../../../hooks/use-handle-new-value"; -import useHandleNodeClass from "../../../hooks/use-handle-node-class"; -import useHandleRefreshButtonPress from "../../../hooks/use-handle-refresh-buttons"; import OutputComponent from "../OutputComponent"; import HandleRenderComponent from "../handleRenderComponent"; import OutputModal from "../outputModal"; @@ -81,7 +79,12 @@ export default function ParameterComponent({ const setNode = useFlowStore((state) => state.setNode); const myData = useTypesStore((state) => state.data); const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); - const [isLoading, setIsLoading] = useState(false); + const postTemplateValue = usePostTemplateValue({ + node: data.node!, + nodeId: data.id, + parameterId: name, + }); + const isLoading = postTemplateValue.isPending; const updateNodeInternals = useUpdateNodeInternals(); const [errorDuplicateKey, setErrorDuplicateKey] = useState(false); const setFilterEdge = useFlowStore((state) => state.setFilterEdge); @@ -131,29 +134,25 @@ export default function ParameterComponent({ } } + const setErrorData = useAlertStore((state) => state.setErrorData); + const output = useShortcutsStore((state) => state.output); useHotkeys(output, handleOutputWShortcut, { preventDefault }); - const { handleOnNewValue: handleOnNewValueHook } = useHandleOnNewValue( - data, - name, - takeSnapshot, - handleUpdateValues, - debouncedHandleUpdateValues, - setNode, - setIsLoading, - ); - const { handleNodeClass: handleNodeClassHook } = useHandleNodeClass( - data, - name, takeSnapshot, setNode, - updateNodeInternals, + data.id, ); - const { handleRefreshButtonPress: handleRefreshButtonPressHook } = - useHandleRefreshButtonPress(setIsLoading, setNode); + const handleRefreshButtonPress = () => + mutateTemplate( + data.node?.template[name]?.value, + data.node!, + handleNodeClass, + postTemplateValue, + setErrorData, + ); let disabled = edges.some( @@ -167,18 +166,24 @@ export default function ParameterComponent({ edge.sourceHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id), ) ?? false; - const handleRefreshButtonPress = async (name, data) => { - handleRefreshButtonPressHook(name, data); - }; + const { handleOnNewValue: handleOnNewValueHook } = useHandleOnNewValue({ + node: data.node!, + nodeId: data.id, + name, + }); - useFetchDataOnMount(data, name, handleUpdateValues, setNode, setIsLoading); - - const handleOnNewValue = async ( - newValue: string | string[] | boolean | Object[], + const handleOnNewValue = ( + value: any, dbValue?: boolean, - skipSnapshot: boolean | undefined = false, - ): Promise => { - handleOnNewValueHook(newValue, dbValue, skipSnapshot); + skipSnapshot?: boolean, + ) => { + handleOnNewValueHook( + { + value, + load_from_db: dbValue, + }, + { skipSnapshot }, + ); }; const handleNodeClass = ( @@ -186,9 +191,11 @@ export default function ParameterComponent({ code?: string, type?: string, ): void => { - handleNodeClassHook(newNodeClass, code, type); + handleNodeClassHook(newNodeClass, name, code, type); }; + useFetchDataOnMount(data.node!, handleNodeClass, name, postTemplateValue); + useEffect(() => { // @ts-ignore infoHtml.current = ( @@ -614,7 +621,7 @@ export default function ParameterComponent({
, + setErrorData, + ) => { + try { + const newNode = cloneDeep(node); + const newTemplate = await postTemplateValue.mutateAsync({ + value: newValue, + }); + if (newTemplate) { + newNode.template = newTemplate; + } + setNodeClass(newNode); + } catch (e) { + const error = e as ResponseErrorDetailAPI; + setErrorData({ + title: TITLE_ERROR_UPDATING_COMPONENT, + list: [error.response?.data?.detail || ERROR_UPDATING_COMPONENT], + }); + } + }, + SAVE_DEBOUNCE_TIME, +); diff --git a/src/frontend/src/CustomNodes/hooks/use-fetch-data-on-mount.tsx b/src/frontend/src/CustomNodes/hooks/use-fetch-data-on-mount.tsx index 497c560c2..6379202fd 100644 --- a/src/frontend/src/CustomNodes/hooks/use-fetch-data-on-mount.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-fetch-data-on-mount.tsx @@ -1,55 +1,40 @@ -import { cloneDeep } from "lodash"; -import { useEffect } from "react"; import { - ERROR_UPDATING_COMPONENT, - TITLE_ERROR_UPDATING_COMPONENT, -} from "../../constants/constants"; + APIClassType, + APITemplateType, + ResponseErrorDetailAPI, +} from "@/types/api"; +import { UseMutationResult } from "@tanstack/react-query"; +import { useEffect } from "react"; import useAlertStore from "../../stores/alertStore"; -import { ResponseErrorDetailAPI } from "../../types/api"; -import { NodeDataType } from "../../types/flow"; +import { mutateTemplate } from "../helpers/mutate-template"; const useFetchDataOnMount = ( - data: NodeDataType, + node: APIClassType, + setNodeClass: (node: APIClassType) => void, name: string, - handleUpdateValues: (name: string, data: NodeDataType) => Promise, - setNode: (id: string, callback: (oldNode: any) => any) => void, - setIsLoading: (value: boolean) => void, + postTemplateValue: UseMutationResult< + APITemplateType | undefined, + ResponseErrorDetailAPI, + any + >, ) => { const setErrorData = useAlertStore((state) => state.setErrorData); useEffect(() => { async function fetchData() { + const template = node.template[name]; if ( - (data.node?.template[name]?.real_time_refresh || - data.node?.template[name]?.refresh_button) && + (template?.real_time_refresh || template?.refresh_button) && // options can be undefined but not an empty array - (data.node?.template[name]?.options?.length ?? 0) === 0 + (template?.options?.length ?? 0) === 0 ) { - setIsLoading(true); - try { - let newTemplate = await handleUpdateValues(name, data); - - if (newTemplate) { - setNode(data.id, (oldNode) => { - let newNode = cloneDeep(oldNode); - newNode.data = { - ...newNode.data, - }; - newNode.data.node.template = newTemplate; - return newNode; - }); - } - } catch (error) { - let responseError = error as ResponseErrorDetailAPI; - - setErrorData({ - title: TITLE_ERROR_UPDATING_COMPONENT, - list: [ - responseError?.response?.data?.detail ?? ERROR_UPDATING_COMPONENT, - ], - }); - } - setIsLoading(false); + mutateTemplate( + template?.value, + node, + setNodeClass, + postTemplateValue, + setErrorData, + ); } } fetchData(); diff --git a/src/frontend/src/CustomNodes/hooks/use-handle-new-value.tsx b/src/frontend/src/CustomNodes/hooks/use-handle-new-value.tsx index 5f294af5b..bf457ff34 100644 --- a/src/frontend/src/CustomNodes/hooks/use-handle-new-value.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-handle-new-value.tsx @@ -1,79 +1,91 @@ +import { usePostTemplateValue } from "@/controllers/API/queries/nodes/use-post-template-value"; +import useAlertStore from "@/stores/alertStore"; +import useFlowStore from "@/stores/flowStore"; +import useFlowsManagerStore from "@/stores/flowsManagerStore"; +import { APIClassType, InputFieldType } from "@/types/api"; import { cloneDeep } from "lodash"; -import { - ERROR_UPDATING_COMPONENT, - TITLE_ERROR_UPDATING_COMPONENT, -} from "../../constants/constants"; -import useAlertStore from "../../stores/alertStore"; -import { ResponseErrorTypeAPI } from "../../types/api"; -import { NodeDataType } from "../../types/flow"; +import { mutateTemplate } from "../helpers/mutate-template"; +const useHandleOnNewValue = ({ + node, + nodeId, + name, +}: { + node: APIClassType; + nodeId: string; + name: string; +}) => { + const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); + + const setNode = useFlowStore((state) => state.setNode); -const useHandleOnNewValue = ( - data: NodeDataType, - name: string, - takeSnapshot: () => void, - handleUpdateValues: (name: string, data: NodeDataType) => Promise, - debouncedHandleUpdateValues: any, - setNode: (id: string, callback: (oldNode: any) => any) => void, - setIsLoading: (value: boolean) => void, -) => { const setErrorData = useAlertStore((state) => state.setErrorData); - const handleOnNewValue = async (newValue, dbValue, skipSnapshot = false) => { - const nodeTemplate = data.node!.template[name]; - const currentValue = nodeTemplate.value; + const postTemplateValue = usePostTemplateValue({ + parameterId: name, + nodeId: nodeId, + node: node, + }); - if (currentValue !== newValue && !skipSnapshot) { - takeSnapshot(); + const handleOnNewValue = async ( + changes: Partial, + options?: { + skipSnapshot?: boolean; + setNodeClass?: (node: APIClassType) => void; + }, + ) => { + const newNode = cloneDeep(node); + const template = newNode.template; + + if (!template) { + setErrorData({ title: "Template not found in the component" }); + return; } + const parameter = template[name]; + + if (!parameter) { + setErrorData({ title: "Parameter not found in the template" }); + return; + } + + if (!options?.skipSnapshot) takeSnapshot(); + + Object.entries(changes).forEach(([key, value]) => { + parameter[key] = value; + }); + const shouldUpdate = - data.node?.template[name].real_time_refresh && - !data.node?.template[name].refresh_button && - currentValue !== newValue; + parameter.real_time_refresh && !parameter.refresh_button; - const typeToDebounce = nodeTemplate.type; + const setNodeClass = (newNodeClass: APIClassType) => { + options?.setNodeClass && options.setNodeClass(newNodeClass); + setNode(nodeId, (oldNode) => { + const newData = cloneDeep(oldNode.data); + newData.node = newNodeClass; + return { + ...oldNode, + data: newData, + }; + }); + }; - nodeTemplate.value = newValue; - - let newTemplate; - if (shouldUpdate) { - setIsLoading(true); - try { - if (["int"].includes(typeToDebounce)) { - newTemplate = await handleUpdateValues(name, data); - } else { - newTemplate = await debouncedHandleUpdateValues(name, data); - } - } catch (error) { - let responseError = error as ResponseErrorTypeAPI; - setErrorData({ - title: TITLE_ERROR_UPDATING_COMPONENT, - list: [ - responseError?.response?.data?.detail.error ?? - ERROR_UPDATING_COMPONENT, - ], - }); - } - setIsLoading(false); + if (shouldUpdate && changes.value) { + mutateTemplate( + changes.value, + newNode, + setNodeClass, + postTemplateValue, + setErrorData, + ); } - setNode(data.id, (oldNode) => { - const newNode = cloneDeep(oldNode); - newNode.data = { - ...newNode.data, + setNode(nodeId, (oldNode) => { + const newData = cloneDeep(oldNode.data); + newData.node = newNode; + return { + ...oldNode, + data: newData, }; - - if (dbValue !== undefined) { - newNode.data.node.template[name].load_from_db = dbValue; - } - - if (data.node?.template[name].real_time_refresh && newTemplate) { - newNode.data.node.template = newTemplate; - } else { - newNode.data.node.template[name].value = newValue; - } - - return newNode; }); }; diff --git a/src/frontend/src/CustomNodes/hooks/use-handle-node-class.tsx b/src/frontend/src/CustomNodes/hooks/use-handle-node-class.tsx index 0827c0b4e..6c65ae4a3 100644 --- a/src/frontend/src/CustomNodes/hooks/use-handle-node-class.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-handle-node-class.tsx @@ -1,37 +1,31 @@ import { cloneDeep } from "lodash"; -import { NodeDataType } from "../../types/flow"; const useHandleNodeClass = ( - data: NodeDataType, - name: string, takeSnapshot: () => void, setNode: (id: string, callback: (oldNode: any) => any) => void, - updateNodeInternals: (id: string) => void, + nodeId: string, ) => { - const handleNodeClass = (newNodeClass, code, type?: string) => { - if (!data.node) return; - if (data.node!.template[name].value !== code) { + const handleNodeClass = (newNodeClass, name, code, type?: string) => { + if (code) { takeSnapshot(); } - setNode(data.id, (oldNode) => { + setNode(nodeId, (oldNode) => { let newNode = cloneDeep(oldNode); newNode.data = { ...newNode.data, - node: newNodeClass, - description: newNodeClass.description ?? data.node!.description, - display_name: newNodeClass.display_name ?? data.node!.display_name, + node: cloneDeep(newNodeClass), }; if (type) { newNode.data.type = type; } - newNode.data.node.template[name].value = code; + if (code) { + newNode.data.node.template[name].value = code; + } return newNode; }); - - updateNodeInternals(data.id); }; return { handleNodeClass }; diff --git a/src/frontend/src/CustomNodes/hooks/use-handle-refresh-buttons.tsx b/src/frontend/src/CustomNodes/hooks/use-handle-refresh-buttons.tsx deleted file mode 100644 index 65345fcea..000000000 --- a/src/frontend/src/CustomNodes/hooks/use-handle-refresh-buttons.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { cloneDeep } from "lodash"; -import { - ERROR_UPDATING_COMPONENT, - TITLE_ERROR_UPDATING_COMPONENT, -} from "../../constants/constants"; -import useAlertStore from "../../stores/alertStore"; -import { ResponseErrorDetailAPI } from "../../types/api"; -import { handleUpdateValues } from "../../utils/parameterUtils"; - -const useHandleRefreshButtonPress = ( - setIsLoading: (value: boolean) => void, - setNode: (id: string, callback: (oldNode: any) => any) => void, -) => { - const setErrorData = useAlertStore((state) => state.setErrorData); - - const handleRefreshButtonPress = async (name, data) => { - setIsLoading(true); - try { - let newTemplate = await handleUpdateValues(name, data); - - if (newTemplate) { - setNode(data.id, (oldNode) => { - let newNode = cloneDeep(oldNode); - newNode.data = { - ...newNode.data, - }; - newNode.data.node.template = newTemplate; - return newNode; - }); - } - } catch (error) { - let responseError = error as ResponseErrorDetailAPI; - - setErrorData({ - title: TITLE_ERROR_UPDATING_COMPONENT, - list: [ - responseError?.response?.data?.detail ?? ERROR_UPDATING_COMPONENT, - ], - }); - } - setIsLoading(false); - }; - - return { handleRefreshButtonPress }; -}; - -export default useHandleRefreshButtonPress; diff --git a/src/frontend/src/components/intComponent/index.tsx b/src/frontend/src/components/intComponent/index.tsx index 7621d6732..fc49e386b 100644 --- a/src/frontend/src/components/intComponent/index.tsx +++ b/src/frontend/src/components/intComponent/index.tsx @@ -1,4 +1,12 @@ -import { useEffect } from "react"; +import { cn } from "@/utils/utils"; +import { + NumberDecrementStepper, + NumberIncrementStepper, + NumberInput, + NumberInputField, + NumberInputStepper, +} from "@chakra-ui/number-input"; +import { useEffect, useRef, useState } from "react"; import { IntComponentType } from "../../types/components"; import { handleKeyDown, @@ -23,32 +31,54 @@ export default function IntComponent({ } }, [disabled, onChange]); + const [cursor, setCursor] = useState(null); + const ref = useRef(null); + + useEffect(() => { + ref.current?.setSelectionRange(cursor, cursor); + }, [ref, cursor, value]); + + const handleChangeInput = (e: React.ChangeEvent) => { + setCursor(e.target.selectionStart); + onChange(e.target.value); + }; + return (
- { - handleOnlyIntegerInput(event); - handleKeyDown(event, value, ""); - }} - type="number" step={rangeSpec?.step ?? 1} min={rangeSpec?.min ?? min} max={rangeSpec?.max ?? undefined} - onInput={(event: React.ChangeEvent) => { - if (Number(event.target.value) < min) { - event.target.value = min.toString(); - } + onChange={(value) => { + onChange(value); }} value={value ?? ""} - className={editNode ? "input-edit-node" : ""} - disabled={disabled} - placeholder={editNode ? "Integer number" : "Type an integer number"} - onChange={(event) => { - onChange(event.target.value); - }} - data-testid={id} - /> + > + { + handleKeyDown(event, value, ""); + }} + onInput={(event: React.ChangeEvent) => { + if (Number(event.target.value) < min) { + event.target.value = min.toString(); + } + }} + disabled={disabled} + placeholder={editNode ? "Integer number" : "Type an integer number"} + data-testid={id} + ref={ref} + /> + + + + +
); } diff --git a/src/frontend/src/components/tableComponent/components/tableNodeCellRender/cellTypeStr.tsx b/src/frontend/src/components/tableComponent/components/tableNodeCellRender/cellTypeStr.tsx index 7b32ab232..260a86242 100644 --- a/src/frontend/src/components/tableComponent/components/tableNodeCellRender/cellTypeStr.tsx +++ b/src/frontend/src/components/tableComponent/components/tableNodeCellRender/cellTypeStr.tsx @@ -51,7 +51,7 @@ export function renderStrType({ editNode={true} disabled={disabled} options={templateData.options || []} - value={templateValue ?? "Choose an option"} + values={[templateValue ?? "Choose an option"]} id={"multiselect-" + templateData.name} onValueChange={(value) => handleOnNewValue(value, templateData.key)} /> diff --git a/src/frontend/src/components/tableComponent/components/tableNodeCellRender/index.tsx b/src/frontend/src/components/tableComponent/components/tableNodeCellRender/index.tsx index 83c1d1d25..60689451f 100644 --- a/src/frontend/src/components/tableComponent/components/tableNodeCellRender/index.tsx +++ b/src/frontend/src/components/tableComponent/components/tableNodeCellRender/index.tsx @@ -1,3 +1,4 @@ +import useHandleOnNewValue from "@/CustomNodes/hooks/use-handle-new-value"; import { CustomCellRendererProps } from "ag-grid-react"; import { cloneDeep } from "lodash"; import { useState } from "react"; @@ -12,39 +13,18 @@ import { import { classNames } from "../../../../utils/utils"; import CodeAreaComponent from "../../../codeAreaComponent"; import DictComponent from "../../../dictComponent"; -import Dropdown from "../../../dropdownComponent"; import FloatComponent from "../../../floatComponent"; import InputFileComponent from "../../../inputFileComponent"; -import InputGlobalComponent from "../../../inputGlobalComponent"; -import InputListComponent from "../../../inputListComponent"; import IntComponent from "../../../intComponent"; import KeypairListComponent from "../../../keypairListComponent"; import PromptAreaComponent from "../../../promptComponent"; -import TextAreaComponent from "../../../textAreaComponent"; import ToggleShadComponent from "../../../toggleShadComponent"; import { renderStrType } from "./cellTypeStr"; export default function TableNodeCellRender({ node: { data }, - value: { - value, - nodeClass, - handleOnNewValue: handleOnNewValueNode, - handleNodeClass, - }, + value: { value, nodeId, nodeClass, handleNodeClass }, }: CustomCellRendererProps) { - const handleOnNewValue = (newValue: any, name: string, dbValue?: boolean) => { - handleOnNewValueNode(newValue, name, dbValue); - setTemplateData((old) => { - let newData = cloneDeep(old); - newData.value = newValue; - if (dbValue !== undefined) { - newData.load_from_db = newValue; - } - return newData; - }); - setTemplateValue(newValue); - }; const setNodeClass = (value: APIClassType, code?: string, type?: string) => { handleNodeClass(value, templateData.key, code, type); }; @@ -54,6 +34,34 @@ export default function TableNodeCellRender({ const [errorDuplicateKey, setErrorDuplicateKey] = useState(false); const edges = useFlowStore((state) => state.edges); + const { handleOnNewValue: handleOnNewValueHook } = useHandleOnNewValue({ + node: nodeClass, + nodeId: nodeId, + name: data.key, + }); + const handleOnNewValue = ( + value: any, + dbValue?: boolean, + skipSnapshot?: boolean, + ) => { + handleOnNewValueHook( + { + value, + load_from_db: dbValue, + }, + { skipSnapshot, setNodeClass }, + ); + setTemplateData((old) => { + let newData = cloneDeep(old); + newData.value = value; + if (dbValue !== undefined) { + newData.load_from_db = value; + } + return newData; + }); + setTemplateValue(value); + }; + const id = { inputTypes: templateData.input_types, type: templateData.type, diff --git a/src/frontend/src/controllers/API/helpers/constants.ts b/src/frontend/src/controllers/API/helpers/constants.ts index 871707887..4775befc0 100644 --- a/src/frontend/src/controllers/API/helpers/constants.ts +++ b/src/frontend/src/controllers/API/helpers/constants.ts @@ -13,6 +13,7 @@ export const URLs = { AUTOLOGIN: "auto_login", REFRESH: "refresh", BUILD: `build`, + CUSTOM_COMPONENT: `custom_component`, } as const; export function getURL(key: keyof typeof URLs, params: any = {}) { diff --git a/src/frontend/src/controllers/API/queries/nodes/use-post-template-value.ts b/src/frontend/src/controllers/API/queries/nodes/use-post-template-value.ts new file mode 100644 index 000000000..73d339bbb --- /dev/null +++ b/src/frontend/src/controllers/API/queries/nodes/use-post-template-value.ts @@ -0,0 +1,61 @@ +import { + APIClassType, + APITemplateType, + ResponseErrorDetailAPI, + useMutationFunctionType, +} from "@/types/api"; +import { UseMutationResult } from "@tanstack/react-query"; +import { api } from "../../api"; +import { getURL } from "../../helpers/constants"; +import { UseRequestProcessor } from "../../services/request-processor"; + +interface IPostTemplateValue { + value: any; +} + +interface IPostTemplateValueParams { + node: APIClassType; + nodeId: string; + parameterId: string; +} + +export const usePostTemplateValue: useMutationFunctionType< + IPostTemplateValueParams, + IPostTemplateValue, + APITemplateType | undefined, + ResponseErrorDetailAPI +> = ({ parameterId, nodeId, node }, options?) => { + const { mutate } = UseRequestProcessor(); + + const postTemplateValueFn = async ( + payload: IPostTemplateValue, + ): Promise => { + const template = node.template; + + if (!template) return; + + const response = await api.post( + getURL("CUSTOM_COMPONENT", { update: "update" }), + { + code: template.code.value, + template: template, + field: parameterId, + field_value: payload.value, + }, + ); + + return response.data.template; + }; + + const mutation: UseMutationResult< + APITemplateType | undefined, + ResponseErrorDetailAPI, + IPostTemplateValue + > = mutate( + ["usePostTemplateValue", { parameterId, nodeId }], + postTemplateValueFn, + options, + ); + + return mutation; +}; diff --git a/src/frontend/src/modals/editNodeModal/hooks/use-column-defs.tsx b/src/frontend/src/modals/editNodeModal/hooks/use-column-defs.tsx index b6230e3a6..c6ccd1b54 100644 --- a/src/frontend/src/modals/editNodeModal/hooks/use-column-defs.tsx +++ b/src/frontend/src/modals/editNodeModal/hooks/use-column-defs.tsx @@ -1,19 +1,18 @@ import { ColDef, ValueGetterParams } from "ag-grid-community"; -import { useMemo, useState } from "react"; +import { useMemo } from "react"; import TableNodeCellRender from "../../../components/tableComponent/components/tableNodeCellRender"; import TableToggleCellRender from "../../../components/tableComponent/components/tableToggleCellRender"; import { APIClassType } from "../../../types/api"; -import { NodeDataType } from "../../../types/flow"; const useColumnDefs = ( nodeClass: APIClassType, - handleOnNewValue: (newValue: any, name: string, setDb?: boolean) => void, handleNodeClass: ( newNodeClass: APIClassType, name: string, code: string, type?: string, ) => void, + nodeId: string, changeAdvanced: (n: string) => void, open: boolean, ) => { @@ -53,8 +52,8 @@ const useColumnDefs = ( valueGetter: (params: ValueGetterParams) => { return { value: params.data.value, + nodeId: nodeId, nodeClass: nodeClass, - handleOnNewValue: handleOnNewValue, handleNodeClass: handleNodeClass, }; }, diff --git a/src/frontend/src/modals/editNodeModal/hooks/use-handle-new-value.tsx b/src/frontend/src/modals/editNodeModal/hooks/use-handle-new-value.tsx deleted file mode 100644 index 58351fd2d..000000000 --- a/src/frontend/src/modals/editNodeModal/hooks/use-handle-new-value.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { cloneDeep } from "lodash"; -import { - ERROR_UPDATING_COMPONENT, - TITLE_ERROR_UPDATING_COMPONENT, -} from "../../../constants/constants"; -import useAlertStore from "../../../stores/alertStore"; -import { ResponseErrorTypeAPI } from "../../../types/api"; -import { NodeDataType } from "../../../types/flow"; - -const useHandleOnNewValue = ( - data: NodeDataType, - takeSnapshot: () => void, - handleUpdateValues: (name: string, data: NodeDataType) => Promise, - debouncedHandleUpdateValues: any, - setNode: (id: string, callback: (oldNode: any) => any) => void, -) => { - const setErrorData = useAlertStore((state) => state.setErrorData); - - const handleOnNewValue = async ( - newValue, - name, - dbValue, - skipSnapshot = false, - ) => { - const nodeTemplate = data.node!.template[name]; - const currentValue = nodeTemplate.value; - - if (currentValue !== newValue && !skipSnapshot) { - takeSnapshot(); - } - - const shouldUpdate = - data.node?.template[name].real_time_refresh && - !data.node?.template[name].refresh_button && - currentValue !== newValue; - - const typeToDebounce = nodeTemplate.type; - - nodeTemplate.value = newValue; - - let newTemplate; - if (shouldUpdate) { - try { - if (["int"].includes(typeToDebounce)) { - newTemplate = await handleUpdateValues(name, data); - } else { - newTemplate = await debouncedHandleUpdateValues(name, data); - } - } catch (error) { - let responseError = error as ResponseErrorTypeAPI; - setErrorData({ - title: TITLE_ERROR_UPDATING_COMPONENT, - list: [ - responseError?.response?.data?.detail.error ?? - ERROR_UPDATING_COMPONENT, - ], - }); - } - } - - setNode(data.id, (oldNode) => { - const newNode = cloneDeep(oldNode); - newNode.data = { - ...newNode.data, - }; - - if (dbValue !== undefined) { - newNode.data.node.template[name].load_from_db = dbValue; - } - - if (data.node?.template[name].real_time_refresh && newTemplate) { - newNode.data.node.template = newTemplate; - } else { - newNode.data.node.template[name].value = newValue; - } - - return newNode; - }); - }; - - return { handleOnNewValue }; -}; - -export default useHandleOnNewValue; diff --git a/src/frontend/src/modals/editNodeModal/hooks/use-handle-node-class.tsx b/src/frontend/src/modals/editNodeModal/hooks/use-handle-node-class.tsx deleted file mode 100644 index 9ad9eec62..000000000 --- a/src/frontend/src/modals/editNodeModal/hooks/use-handle-node-class.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { cloneDeep } from "lodash"; -import { NodeDataType } from "../../../types/flow"; - -const useHandleNodeClass = ( - data: NodeDataType, - takeSnapshot: () => void, - setNode: (id: string, callback: (oldNode: any) => any) => void, - updateNodeInternals: (id: string) => void, -) => { - const handleNodeClass = (newNodeClass, name, code, type?: string) => { - if (!data.node) return; - if (data.node!.template[name].value !== code) { - takeSnapshot(); - } - - setNode(data.id, (oldNode) => { - let newNode = cloneDeep(oldNode); - - newNode.data = { - ...newNode.data, - node: newNodeClass, - description: newNodeClass.description ?? data.node!.description, - display_name: newNodeClass.display_name ?? data.node!.display_name, - }; - if (type) { - newNode.data.node.template[name].type = type; - } - newNode.data.node.template[name].value = code; - - return newNode; - }); - - updateNodeInternals(data.id); - }; - - return { handleNodeClass }; -}; - -export default useHandleNodeClass; diff --git a/src/frontend/src/modals/editNodeModal/index.tsx b/src/frontend/src/modals/editNodeModal/index.tsx index 0fb52b128..881158df1 100644 --- a/src/frontend/src/modals/editNodeModal/index.tsx +++ b/src/frontend/src/modals/editNodeModal/index.tsx @@ -1,6 +1,7 @@ import { ColDef } from "ag-grid-community"; import { forwardRef, useState } from "react"; import { useUpdateNodeInternals } from "reactflow"; +import useHandleNodeClass from "../../CustomNodes/hooks/use-handle-node-class"; import TableComponent from "../../components/tableComponent"; import { Badge } from "../../components/ui/badge"; import { Button } from "../../components/ui/button"; @@ -9,15 +10,9 @@ import useFlowStore from "../../stores/flowStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore"; import { APIClassType } from "../../types/api"; import { NodeDataType } from "../../types/flow"; -import { - debouncedHandleUpdateValues, - handleUpdateValues, -} from "../../utils/parameterUtils"; import BaseModal from "../baseModal"; import useColumnDefs from "./hooks/use-column-defs"; import useHandleChangeAdvanced from "./hooks/use-handle-change-advanced"; -import useHandleOnNewValue from "./hooks/use-handle-new-value"; -import useHandleNodeClass from "./hooks/use-handle-node-class"; import useRowData from "./hooks/use-row-data"; const EditNodeModal = forwardRef( @@ -40,19 +35,10 @@ const EditNodeModal = forwardRef( const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); const updateNodeInternals = useUpdateNodeInternals(); - const { handleOnNewValue: handleOnNewValueHook } = useHandleOnNewValue( - data, - takeSnapshot, - handleUpdateValues, - debouncedHandleUpdateValues, - setNode, - ); - const { handleNodeClass: handleNodeClassHook } = useHandleNodeClass( - data, takeSnapshot, setNode, - updateNodeInternals, + data.id, ); const [nodeClass, setNodeClass] = useState(data.node!); @@ -74,8 +60,8 @@ const EditNodeModal = forwardRef( const columnDefs: ColDef[] = useColumnDefs( nodeClass, - handleOnNewValueHook, handleNodeClass, + data.id, handleChangeAdvancedHook, open, ); diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx index f3cbebf96..50a8fa23e 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx @@ -1,4 +1,7 @@ +import useHandleOnNewValue from "@/CustomNodes/hooks/use-handle-new-value"; +import useHandleNodeClass from "@/CustomNodes/hooks/use-handle-node-class"; import { usePostRetrieveVertexOrder } from "@/controllers/API/queries/vertex"; +import { APIClassType } from "@/types/api"; import _, { cloneDeep } from "lodash"; import { useEffect, useState } from "react"; import { useHotkeys } from "react-hotkeys-hook"; @@ -22,7 +25,6 @@ import useFlowStore from "../../../../stores/flowStore"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; import { useShortcutsStore } from "../../../../stores/shortcuts"; import { useStoreStore } from "../../../../stores/storeStore"; -import { APIClassType } from "../../../../types/api"; import { nodeToolbarPropsType } from "../../../../types/components"; import { FlowType } from "../../../../types/flow"; import { @@ -31,7 +33,7 @@ import { expandGroupNode, updateFlowPosition, } from "../../../../utils/reactflowUtils"; -import { classNames, cn, isThereModal } from "../../../../utils/utils"; +import { classNames, cn } from "../../../../utils/utils"; import isWrappedWithClass from "../PageComponent/utils/is-wrapped-with-class"; import ToolbarSelectItem from "./toolbarSelectItem"; @@ -384,57 +386,28 @@ export default function NodeToolbarComponent({ const setNode = useFlowStore((state) => state.setNode); - const handleOnNewValue = ( - newValue: string | string[] | boolean | Object[], - ): void => { - if (data.node!.template[name].value !== newValue) { - takeSnapshot(); - } + const { handleOnNewValue: handleOnNewValueHook } = useHandleOnNewValue({ + node: data.node!, + nodeId: data.id, + name, + }); - data.node!.template[name].value = newValue; // necessary to enable ctrl+z inside the input - - setNode(data.id, (oldNode) => { - let newNode = cloneDeep(oldNode); - - newNode.data = { - ...newNode.data, - }; - - newNode.data.node.template[name].value = newValue; - - return newNode; - }); + const handleOnNewValue = (value: string | string[]) => { + handleOnNewValueHook({ value }); }; + const { handleNodeClass: handleNodeClassHook } = useHandleNodeClass( + takeSnapshot, + setNode, + data.id, + ); + const handleNodeClass = ( newNodeClass: APIClassType, - code?: string, - type?: string, - ): void => { - if (!data.node) return; - if (data.node!.template[name].value !== code) { - takeSnapshot(); - } - - setNode(data.id, (oldNode) => { - let newNode = cloneDeep(oldNode); - - newNode.data = { - ...newNode.data, - node: newNodeClass, - description: newNodeClass.description ?? data.node!.description, - display_name: newNodeClass.display_name ?? data.node!.display_name, - }; - - if (type) { - newNode.data.type = type; - } - - newNode.data.node.template[name].value = code; - - return newNode; - }); - updateNodeInternals(data.id); + code: string, + type: string, + ) => { + handleNodeClassHook(newNodeClass, name, code, type); }; const [openModal, setOpenModal] = useState(false); diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index 7c5c60579..85deddbc5 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -171,7 +171,7 @@ export type CodeAreaComponentType = { value: string; editNode?: boolean; nodeClass?: APIClassType; - setNodeClass?: (value: APIClassType, code?: string) => void; + setNodeClass?: (value: APIClassType, code: string, type: string) => void; dynamic?: boolean; id?: string; readonly?: boolean; @@ -612,8 +612,8 @@ export type codeAreaModalPropsType = { nodeClass: APIClassType | undefined; setNodeClass: ( Class: APIClassType, - code?: string, - type?: string, + code: string, + type: string, ) => void | undefined; children: ReactNode; dynamic?: boolean;