@elyx-code/editor-ui 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (452) hide show
  1. package/README.md +2 -0
  2. package/package.json +109 -0
  3. package/src/App.tsx +31 -0
  4. package/src/Router.tsx +115 -0
  5. package/src/__mocks__/defaultModuleMock.ts +1 -0
  6. package/src/__mocks__/fileMock.ts +1 -0
  7. package/src/__mocks__/styleMock.ts +1 -0
  8. package/src/assets/Clock-11.1s-18px.svg +16 -0
  9. package/src/assets/Clock-11.1s-28px.svg +16 -0
  10. package/src/assets/authentication.svg +1 -0
  11. package/src/assets/canvas-backdrop-0.png +0 -0
  12. package/src/assets/canvas-backdrop-1.png +0 -0
  13. package/src/assets/canvas-backdrop-2.png +0 -0
  14. package/src/assets/canvas-backdrop-3.png +0 -0
  15. package/src/assets/canvas-backdrop-4.png +0 -0
  16. package/src/assets/canvas-backdrop-5.png +0 -0
  17. package/src/assets/canvas-backdrop.png +0 -0
  18. package/src/assets/checkmark-animation.gif +0 -0
  19. package/src/assets/checkmark-animation.mp4 +0 -0
  20. package/src/assets/code-formatting/format-black.svg +6 -0
  21. package/src/assets/code-formatting/format-dark-grey.svg +6 -0
  22. package/src/assets/code-formatting/format-light-grey.svg +6 -0
  23. package/src/assets/code-formatting/format-white.svg +6 -0
  24. package/src/assets/code-formatting/inline-black.svg +5 -0
  25. package/src/assets/code-formatting/inline-dark-grey.svg +5 -0
  26. package/src/assets/code-formatting/inline-light-grey.svg +5 -0
  27. package/src/assets/code-formatting/inline-white.svg +5 -0
  28. package/src/assets/contained-logo-full-word.png +0 -0
  29. package/src/assets/cron-job-color.png +0 -0
  30. package/src/assets/cron-job.png +0 -0
  31. package/src/assets/database-table-color.png +0 -0
  32. package/src/assets/database-table.png +0 -0
  33. package/src/assets/datatype-icons/black/any.svg +1 -0
  34. package/src/assets/datatype-icons/black/binary.svg +1 -0
  35. package/src/assets/datatype-icons/black/boolean.svg +3 -0
  36. package/src/assets/datatype-icons/black/date-time.svg +3 -0
  37. package/src/assets/datatype-icons/black/definition-entity.svg +6 -0
  38. package/src/assets/datatype-icons/black/key-file.svg +1 -0
  39. package/src/assets/datatype-icons/black/list.svg +3 -0
  40. package/src/assets/datatype-icons/black/null.svg +3 -0
  41. package/src/assets/datatype-icons/black/number.svg +13 -0
  42. package/src/assets/datatype-icons/black/project.svg +12 -0
  43. package/src/assets/datatype-icons/black/sql-program.svg +2 -0
  44. package/src/assets/datatype-icons/black/text.svg +3 -0
  45. package/src/assets/datatype-icons/black/unknown.svg +3 -0
  46. package/src/assets/datatype-icons/black/uuid.svg +4 -0
  47. package/src/assets/datatype-icons/black/void.svg +1 -0
  48. package/src/assets/datatype-icons/dark-grey/any.svg +1 -0
  49. package/src/assets/datatype-icons/dark-grey/boolean.svg +3 -0
  50. package/src/assets/datatype-icons/dark-grey/date-time.svg +3 -0
  51. package/src/assets/datatype-icons/dark-grey/definition-entity.svg +6 -0
  52. package/src/assets/datatype-icons/dark-grey/list.svg +3 -0
  53. package/src/assets/datatype-icons/dark-grey/null.svg +3 -0
  54. package/src/assets/datatype-icons/dark-grey/number.svg +13 -0
  55. package/src/assets/datatype-icons/dark-grey/project.svg +12 -0
  56. package/src/assets/datatype-icons/dark-grey/sql-program.svg +2 -0
  57. package/src/assets/datatype-icons/dark-grey/text.svg +3 -0
  58. package/src/assets/datatype-icons/dark-grey/unknown.svg +3 -0
  59. package/src/assets/datatype-icons/dark-grey/uuid.svg +4 -0
  60. package/src/assets/datatype-icons/dark-grey/void.svg +1 -0
  61. package/src/assets/datatype-icons/light-grey/any.svg +1 -0
  62. package/src/assets/datatype-icons/light-grey/boolean.svg +3 -0
  63. package/src/assets/datatype-icons/light-grey/date-time.svg +3 -0
  64. package/src/assets/datatype-icons/light-grey/definition-entity.svg +6 -0
  65. package/src/assets/datatype-icons/light-grey/list.svg +3 -0
  66. package/src/assets/datatype-icons/light-grey/null.svg +3 -0
  67. package/src/assets/datatype-icons/light-grey/number.svg +13 -0
  68. package/src/assets/datatype-icons/light-grey/project.svg +12 -0
  69. package/src/assets/datatype-icons/light-grey/sql-program.svg +2 -0
  70. package/src/assets/datatype-icons/light-grey/text.svg +3 -0
  71. package/src/assets/datatype-icons/light-grey/unknown.svg +3 -0
  72. package/src/assets/datatype-icons/light-grey/uuid.svg +4 -0
  73. package/src/assets/datatype-icons/light-grey/void.svg +1 -0
  74. package/src/assets/edit.png +0 -0
  75. package/src/assets/execution.svg +13 -0
  76. package/src/assets/favicon.svg +14 -0
  77. package/src/assets/file-search.svg +1 -0
  78. package/src/assets/http-endpoint.png +0 -0
  79. package/src/assets/image-input-placeholder.png +0 -0
  80. package/src/assets/logo-full-word-white.png +0 -0
  81. package/src/assets/logo-full-word.png +0 -0
  82. package/src/assets/password.svg +85 -0
  83. package/src/assets/pencil.png +0 -0
  84. package/src/assets/publish-project-rich-icon-2.svg +1 -0
  85. package/src/assets/publish-project-rich-icon.svg +1 -0
  86. package/src/assets/relational-database.png +0 -0
  87. package/src/assets/resources.svg +3 -0
  88. package/src/assets/resume-icon-14px.png +0 -0
  89. package/src/assets/server.png +0 -0
  90. package/src/assets/small-status/checkmark.svg +4 -0
  91. package/src/assets/small-status/error.svg +4 -0
  92. package/src/assets/small-status/loading.svg +4 -0
  93. package/src/assets/small-status/skipped.svg +11 -0
  94. package/src/assets/sql-connection-config.svg +1 -0
  95. package/src/assets/sql-row-transformer.svg +1 -0
  96. package/src/assets/ssl-certificate-config.svg +1 -0
  97. package/src/assets/sync.svg +1 -0
  98. package/src/assets/testing-logic-icon.svg +1 -0
  99. package/src/assets/versions.svg +25 -0
  100. package/src/assets/visual-programming-icon.svg +1 -0
  101. package/src/assets/warning-sign-24px.png +0 -0
  102. package/src/auth/index.ts +318 -0
  103. package/src/components/DialogLoader.tsx +94 -0
  104. package/src/components/EntityDialogHeader.tsx +110 -0
  105. package/src/components/EntityDialogSectionHeader.tsx +214 -0
  106. package/src/components/GalleryAddExternalIntegrationInfoDialog.tsx +87 -0
  107. package/src/components/GenerateProjectStartingLogicPromptDialog.tsx +281 -0
  108. package/src/components/LegacyRouteRedirector.tsx +55 -0
  109. package/src/components/ProPlanChip.tsx +23 -0
  110. package/src/components/ReportBugDialog.tsx +412 -0
  111. package/src/components/RequestIntegrationAccessDialog.tsx +261 -0
  112. package/src/components/UseTemplateProjectDialog.tsx +193 -0
  113. package/src/components/WorkspaceLayout.tsx +152 -0
  114. package/src/components/animated-svg/AnimatedCheckmark.tsx +41 -0
  115. package/src/components/animated-svg/AnimatedCrossmark.tsx +51 -0
  116. package/src/components/animated-svg/AnimatedEmailSending.tsx +38 -0
  117. package/src/components/animated-svg/AnimatedLoading.tsx +72 -0
  118. package/src/components/animated-svg/animated-svg.css +239 -0
  119. package/src/components/canvas/Canvas.tsx +16 -0
  120. package/src/components/canvas/CreateEntityMenu.tsx +2020 -0
  121. package/src/components/canvas/canvas.css +10 -0
  122. package/src/components/canvas/create-entity-menu.css +579 -0
  123. package/src/components/canvas-search/CanvasSearch.tsx +501 -0
  124. package/src/components/canvas-search/canvas-search.css +126 -0
  125. package/src/components/canvas-settings-menu/CanvasSettingsMenuButton.tsx +515 -0
  126. package/src/components/canvas-settings-menu/canvas-settings-menu.css +96 -0
  127. package/src/components/circular-image-upload/CircularImageUpload.tsx +113 -0
  128. package/src/components/circular-image-upload/circular-image-upload.css +69 -0
  129. package/src/components/costs/CostsDialog.tsx +459 -0
  130. package/src/components/data-type/DataTypeBuilder.tsx +3127 -0
  131. package/src/components/data-type/data-type-builder.css +45 -0
  132. package/src/components/dialogs/BetaAcknowledgeDialog.tsx +43 -0
  133. package/src/components/dialogs/ComplexDataDialog.tsx +458 -0
  134. package/src/components/dialogs/CronBuilderDialog.tsx +2145 -0
  135. package/src/components/dialogs/ExternalIntegrationConnections.tsx +565 -0
  136. package/src/components/dialogs/JsonEditorDialog.tsx +1392 -0
  137. package/src/components/dialogs/StringEditorDialog.tsx +268 -0
  138. package/src/components/dialogs/argument-declaration/ArgumentDeclaration.tsx +1167 -0
  139. package/src/components/dialogs/argument-declaration/ArgumentDeclarationDialogContent.tsx +128 -0
  140. package/src/components/dialogs/beta-dialog.css +165 -0
  141. package/src/components/dialogs/condition/Condition.tsx +431 -0
  142. package/src/components/dialogs/condition/ConditionDialogContent.tsx +126 -0
  143. package/src/components/dialogs/definition-entity/DefinitionEntityDialogContent.tsx +973 -0
  144. package/src/components/dialogs/function-call/FunctionCall.tsx +442 -0
  145. package/src/components/dialogs/function-call/FunctionCallDialogContent.tsx +126 -0
  146. package/src/components/dialogs/function-declaration/FunctionDeclaration.tsx +926 -0
  147. package/src/components/dialogs/function-declaration/FunctionDeclarationDialogContent.tsx +124 -0
  148. package/src/components/dialogs/generating-project-starting-logic-overlay/GeneratingProjectStartingLogicOverlay.tsx +176 -0
  149. package/src/components/dialogs/generating-project-starting-logic-overlay/generating-project-starting-logic-overlay.css +13 -0
  150. package/src/components/dialogs/global-event/GlobalEvent.tsx +475 -0
  151. package/src/components/dialogs/global-event/GlobalEventDialogContent.tsx +126 -0
  152. package/src/components/dialogs/help/HelpDialog.tsx +217 -0
  153. package/src/components/dialogs/help/HelpDilalogHomeContent.tsx +178 -0
  154. package/src/components/dialogs/help/help-dialog.css +116 -0
  155. package/src/components/dialogs/help/help-icon/HelpIconButton.tsx +41 -0
  156. package/src/components/dialogs/help/help-icon/help-icon.css +9 -0
  157. package/src/components/dialogs/input-map/InputMap.tsx +635 -0
  158. package/src/components/dialogs/input-map/InputMapDialogContent.tsx +126 -0
  159. package/src/components/dialogs/json-editor-dialog.css +4 -0
  160. package/src/components/dialogs/loop/Loop.tsx +650 -0
  161. package/src/components/dialogs/loop/LoopDialogContent.tsx +122 -0
  162. package/src/components/dialogs/operation/Operation.tsx +440 -0
  163. package/src/components/dialogs/operation/OperationDialogContent.tsx +126 -0
  164. package/src/components/dialogs/output-map/OutputMap.tsx +536 -0
  165. package/src/components/dialogs/output-map/OutputMapDialogContent.tsx +126 -0
  166. package/src/components/dialogs/property/Property.tsx +1490 -0
  167. package/src/components/dialogs/property/PropertyDialogContent.tsx +106 -0
  168. package/src/components/dialogs/search-statement/ColumnSelector.tsx +334 -0
  169. package/src/components/dialogs/search-statement/ConditionBuilder.tsx +750 -0
  170. package/src/components/dialogs/search-statement/DataAggregationSection.tsx +621 -0
  171. package/src/components/dialogs/search-statement/DataSourceSelection.tsx +734 -0
  172. package/src/components/dialogs/search-statement/EntityMetadataSection.tsx +135 -0
  173. package/src/components/dialogs/search-statement/FilterConditionsSection.tsx +151 -0
  174. package/src/components/dialogs/search-statement/InlineInputMap.tsx +153 -0
  175. package/src/components/dialogs/search-statement/LiteralValue.tsx +616 -0
  176. package/src/components/dialogs/search-statement/MainSourceAndInputsSection.tsx +271 -0
  177. package/src/components/dialogs/search-statement/NestedSearchStatementBuilder.tsx +170 -0
  178. package/src/components/dialogs/search-statement/OutputFormatSection.tsx +1779 -0
  179. package/src/components/dialogs/search-statement/ResultsSection.tsx +344 -0
  180. package/src/components/dialogs/search-statement/SearchStatementBuilder.tsx +251 -0
  181. package/src/components/dialogs/search-statement/SearchStatementDialogContent.tsx +398 -0
  182. package/src/components/dialogs/search-statement/ValueSelector.tsx +766 -0
  183. package/src/components/dialogs/search-statement/search-statement-context.tsx +1630 -0
  184. package/src/components/dialogs/search-statement/search-statement-dialog.css +56 -0
  185. package/src/components/dialogs/search-statement/test.sql +111 -0
  186. package/src/components/dialogs/value-descriptor/ValueDescriptor.tsx +824 -0
  187. package/src/components/dialogs/value-descriptor/ValueDescriptorDialogContent.tsx +124 -0
  188. package/src/components/dialogs/variable-declaration/VariableDeclaration.tsx +836 -0
  189. package/src/components/dialogs/variable-declaration/VariableDeclarationDialogContent.tsx +106 -0
  190. package/src/components/dialogs/variable-instance/VariableInstance.tsx +443 -0
  191. package/src/components/dialogs/variable-instance/VariableInstanceDialogContent.tsx +124 -0
  192. package/src/components/draggable-entity-card/ArgumentDeclaration.tsx +736 -0
  193. package/src/components/draggable-entity-card/CollapseEntityButton.tsx +170 -0
  194. package/src/components/draggable-entity-card/ConditionCard.tsx +1062 -0
  195. package/src/components/draggable-entity-card/ConnectionDeleteButton.tsx +309 -0
  196. package/src/components/draggable-entity-card/DataTypeIcon.tsx +624 -0
  197. package/src/components/draggable-entity-card/DraggableEntityCard.tsx +617 -0
  198. package/src/components/draggable-entity-card/ErrorMapProperty.tsx +464 -0
  199. package/src/components/draggable-entity-card/EventCard.tsx +700 -0
  200. package/src/components/draggable-entity-card/ExecutionInProgressValue.tsx +327 -0
  201. package/src/components/draggable-entity-card/FunctionDeclarationCard.tsx +819 -0
  202. package/src/components/draggable-entity-card/InputMapProperty.tsx +1067 -0
  203. package/src/components/draggable-entity-card/InternalCall.tsx +978 -0
  204. package/src/components/draggable-entity-card/InternalCallExecutionNode.tsx +643 -0
  205. package/src/components/draggable-entity-card/LogicScopeCallerNode.tsx +262 -0
  206. package/src/components/draggable-entity-card/LoopCard.tsx +791 -0
  207. package/src/components/draggable-entity-card/MainValueInput.tsx +523 -0
  208. package/src/components/draggable-entity-card/MainValueOutput.tsx +458 -0
  209. package/src/components/draggable-entity-card/MethodDeclaration.tsx +1088 -0
  210. package/src/components/draggable-entity-card/NestedCondition.tsx +1025 -0
  211. package/src/components/draggable-entity-card/OutputMapProperty.tsx +843 -0
  212. package/src/components/draggable-entity-card/PassthroughEntityCard.tsx +1247 -0
  213. package/src/components/draggable-entity-card/ReturnedError.tsx +549 -0
  214. package/src/components/draggable-entity-card/SmallSuccessFailureNodes.tsx +523 -0
  215. package/src/components/draggable-entity-card/SuccessFailureNodes.tsx +509 -0
  216. package/src/components/draggable-entity-card/TestEntityButton.tsx +946 -0
  217. package/src/components/draggable-entity-card/TestMenu.tsx +523 -0
  218. package/src/components/draggable-entity-card/TestMenuValidationDropdown.tsx +84 -0
  219. package/src/components/draggable-entity-card/UnreachableMarker.tsx +114 -0
  220. package/src/components/draggable-entity-card/VariableCard.tsx +1577 -0
  221. package/src/components/draggable-entity-card/VariableScopeMarker.tsx +117 -0
  222. package/src/components/draggable-entity-card/collapse-entity-button.css +44 -0
  223. package/src/components/draggable-entity-card/definition-entity/DefinitionEntityCard.tsx +1181 -0
  224. package/src/components/draggable-entity-card/definition-entity/DefinitionEntityIcon.tsx +36 -0
  225. package/src/components/draggable-entity-card/definition-entity/DefinitionEntityProperty.tsx +478 -0
  226. package/src/components/draggable-entity-card/definition-entity/DynamicFooterActions.tsx +112 -0
  227. package/src/components/draggable-entity-card/definition-entity/actions/external-integration-connection/ExportCredentialsFooterAction.tsx +461 -0
  228. package/src/components/draggable-entity-card/definition-entity/actions/external-integration-connection/RestablishConnectionFooterAction.tsx +199 -0
  229. package/src/components/draggable-entity-card/definition-entity/actions/external-integration-connection/restablish-connection-footer-action.css +85 -0
  230. package/src/components/draggable-entity-card/definition-entity/actions/google-drive/GoogleDriveFilePickerAPIFooterAction.tsx +277 -0
  231. package/src/components/draggable-entity-card/definition-entity/actions/google-drive/google-drive-file-picker-api-footer-action.css +107 -0
  232. package/src/components/draggable-entity-card/definition-entity/actions/persisted-entity/DatabaseFooterAction.tsx +452 -0
  233. package/src/components/draggable-entity-card/definition-entity/actions/persisted-entity/database-footer-action.css +86 -0
  234. package/src/components/draggable-entity-card/definition-entity/definition-entity-card.css +17 -0
  235. package/src/components/draggable-entity-card/draggable-entity-card.css +1140 -0
  236. package/src/components/draggable-entity-card/entity-locked-icon/EntityLockedIcon.tsx +133 -0
  237. package/src/components/draggable-entity-card/entity-locked-icon/entity-locked.css +8 -0
  238. package/src/components/draggable-entity-card/expand-properties-icon-button/ExpandPropertiesIconButton.tsx +84 -0
  239. package/src/components/draggable-entity-card/expand-properties-icon-button/expand-properties-icon-button.css +21 -0
  240. package/src/components/draggable-entity-card/implement-entity-icon/ImplementEntityIcon.tsx +74 -0
  241. package/src/components/draggable-entity-card/implement-entity-icon/implement-entity-icon.css +13 -0
  242. package/src/components/draggable-entity-card/logic-error/LogicErrorIconMenu.tsx +424 -0
  243. package/src/components/draggable-entity-card/logic-error/logic-error.css +23 -0
  244. package/src/components/draggable-entity-card/new-card-input-button/NewCardInputButton.tsx +193 -0
  245. package/src/components/draggable-entity-card/new-card-input-button/NewDynamicInputButton.tsx +214 -0
  246. package/src/components/draggable-entity-card/new-card-input-button/new-card-input-button.css +71 -0
  247. package/src/components/draggable-entity-card/new-card-output-button/NewCardOutputButton.tsx +192 -0
  248. package/src/components/draggable-entity-card/new-card-output-button/new-card-output-button.css +71 -0
  249. package/src/components/draggable-entity-card/termination-statement/TerminationStatementCard.tsx +1543 -0
  250. package/src/components/draggable-entity-card/termination-statement/termination-statement-card.css +17 -0
  251. package/src/components/draggable-entity-card/test-entity-button.css +55 -0
  252. package/src/components/draggable-entity-card/test-menu.css +181 -0
  253. package/src/components/draggable-entity-card/unreachable-marker.css +43 -0
  254. package/src/components/draggable-entity-card/variable-scope-marker.css +22 -0
  255. package/src/components/dynamic-value/DynamicValue.tsx +2395 -0
  256. package/src/components/dynamic-value/DynamicValueEntry.tsx +1957 -0
  257. package/src/components/dynamic-value/dynamic-value.css +230 -0
  258. package/src/components/editor/ElyxMonacoEditor.tsx +38 -0
  259. package/src/components/entity-error/EntityErrorListItem.tsx +47 -0
  260. package/src/components/entity-error/entity-error.css +198 -0
  261. package/src/components/entity-icon/EntityIcon.tsx +292 -0
  262. package/src/components/entity-icon/entity-icon.css +39 -0
  263. package/src/components/gallery-card/CreateNewProject.tsx +222 -0
  264. package/src/components/gallery-card/GalleryCard.tsx +171 -0
  265. package/src/components/gallery-card/MarketplaceCard.tsx +87 -0
  266. package/src/components/gallery-card/ProjectDuplicationCard.tsx +575 -0
  267. package/src/components/gallery-card/gallery-card.css +25 -0
  268. package/src/components/notifications/NotificationsIconButton.tsx +124 -0
  269. package/src/components/notifications/NotificationsPanel.tsx +385 -0
  270. package/src/components/notifications/notifications.css +189 -0
  271. package/src/components/online-users/LocalOnlineUsers.tsx +175 -0
  272. package/src/components/online-users/PageOnlineUsers.tsx +297 -0
  273. package/src/components/online-users/online-users.css +72 -0
  274. package/src/components/page-backdrop/PageBackdrop.tsx +8 -0
  275. package/src/components/page-backdrop/page-backdrop.css +7 -0
  276. package/src/components/project-configuration/DeleteProjectConfirmationDialog.tsx +134 -0
  277. package/src/components/project-configuration/ProjectConfigurationDialog.tsx +972 -0
  278. package/src/components/project-configuration/ProjectDataForm.tsx +121 -0
  279. package/src/components/project-configuration/UnpublishProjectConfirmationDialog.tsx +162 -0
  280. package/src/components/project-configuration/project-configuration-content.css +209 -0
  281. package/src/components/project-name/ProjectName.tsx +2025 -0
  282. package/src/components/project-name/project-name.css +599 -0
  283. package/src/components/publishing/Publication.tsx +133 -0
  284. package/src/components/publishing/history/PublicationHistoryContent.tsx +414 -0
  285. package/src/components/publishing/history/PublicationHistoryDialog.tsx +234 -0
  286. package/src/components/publishing/preview/PublicationPreviewDialog.tsx +1158 -0
  287. package/src/components/publishing/preview/PublishingPriceForecast.tsx +160 -0
  288. package/src/components/publishing/preview/PublishingResourcesDetails.tsx +91 -0
  289. package/src/components/publishing/publication-sequence/PublishingSequenceContent.tsx +375 -0
  290. package/src/components/publishing/publication-sequence/PublishingSequenceDialog.tsx +344 -0
  291. package/src/components/publishing/publishing-dialog.css +142 -0
  292. package/src/components/publishing/utils.ts +227 -0
  293. package/src/components/resources/ResourcesDialog.tsx +591 -0
  294. package/src/components/resources/UpgradeBanner.tsx +102 -0
  295. package/src/components/resources/codebase/CodebaseDetails.tsx +156 -0
  296. package/src/components/resources/cron-job/CronJobsList.tsx +532 -0
  297. package/src/components/resources/functions/FunctionsList.tsx +454 -0
  298. package/src/components/resources/http-api/HttpAPI.tsx +566 -0
  299. package/src/components/resources/http-api/HttpAPIClientModule.tsx +37 -0
  300. package/src/components/resources/logs/LogsViewer.tsx +768 -0
  301. package/src/components/resources/query.ts +74 -0
  302. package/src/components/resources/relational-database/DatabaseTable.tsx +905 -0
  303. package/src/components/resources/relational-database/RelationalDatabase.tsx +83 -0
  304. package/src/components/resources/relational-database/RelationalDatabaseSecrets.tsx +361 -0
  305. package/src/components/resources/resources-dialog.css +74 -0
  306. package/src/components/test-relational-database/DatabaseTable.tsx +913 -0
  307. package/src/components/test-relational-database/TestDatabaseDialogContent.tsx +670 -0
  308. package/src/components/test-relational-database/query.ts +74 -0
  309. package/src/components/toolbar/ToolBar.tsx +236 -0
  310. package/src/components/toolbar/toolbar.css +78 -0
  311. package/src/components/transaction-history/TransactionHistoryDialog.tsx +268 -0
  312. package/src/components/user/CurrentUserAvatar.tsx +65 -0
  313. package/src/components/user/UserChip.tsx +62 -0
  314. package/src/components/user/user.css +39 -0
  315. package/src/components/user-profile/ChangePasswordForm.tsx +67 -0
  316. package/src/components/user-profile/OwnUserProfileContent.tsx +665 -0
  317. package/src/components/user-profile/PublicUserProfileContent.tsx +99 -0
  318. package/src/components/user-profile/UserDataForm.tsx +75 -0
  319. package/src/components/user-profile/UserProfileDialog.tsx +110 -0
  320. package/src/components/user-profile/user-profile-content.css +25 -0
  321. package/src/config.ts +130 -0
  322. package/src/globals.d.ts +13 -0
  323. package/src/index.html +27 -0
  324. package/src/index.tsx +23 -0
  325. package/src/lib/badge/Badge.tsx +35 -0
  326. package/src/lib/badge/badge.css +32 -0
  327. package/src/lib/button/Button.tsx +129 -0
  328. package/src/lib/button/button.css +145 -0
  329. package/src/lib/canvas/canvas-undo-redo.ts +263 -0
  330. package/src/lib/canvas/defs.ts +170 -0
  331. package/src/lib/canvas/index.test.ts +189 -0
  332. package/src/lib/canvas/index.ts +6999 -0
  333. package/src/lib/canvas/utils.ts +59 -0
  334. package/src/lib/card/Card.tsx +62 -0
  335. package/src/lib/card/LoadingCard.tsx +82 -0
  336. package/src/lib/card/card.css +259 -0
  337. package/src/lib/chip/Chip.tsx +79 -0
  338. package/src/lib/chip/chip.css +0 -0
  339. package/src/lib/dialog/Dialog.tsx +122 -0
  340. package/src/lib/dialog/SmallDialog.tsx +61 -0
  341. package/src/lib/dialog/dialog.css +40 -0
  342. package/src/lib/display-data-structure/index.tsx +21 -0
  343. package/src/lib/dropdown/CanvasDropdownMenuCard.tsx +68 -0
  344. package/src/lib/dropdown/CanvasDropdownMenuCardOption.tsx +136 -0
  345. package/src/lib/dropdown/DropdownButton.tsx +104 -0
  346. package/src/lib/dropdown/DropdownMenuCard.tsx +324 -0
  347. package/src/lib/dropdown/DropdownMenuPopup.tsx +27 -0
  348. package/src/lib/dropdown/dropdown-button.css +76 -0
  349. package/src/lib/dropdown/dropdown-menu.css +151 -0
  350. package/src/lib/json-editor/RawJsonEditor.tsx +137 -0
  351. package/src/lib/json-editor/json-editor.css +35 -0
  352. package/src/lib/loader/Loader.tsx +120 -0
  353. package/src/lib/loader/loader.css +38 -0
  354. package/src/lib/pagination/Pagination.tsx +64 -0
  355. package/src/lib/popup/CanvasPopupBaseComponent.tsx +103 -0
  356. package/src/lib/popup/Popup.tsx +243 -0
  357. package/src/lib/popup/popup.css +16 -0
  358. package/src/lib/table/RowForm.tsx +301 -0
  359. package/src/lib/table/Table.tsx +1069 -0
  360. package/src/lib/table/table.css +249 -0
  361. package/src/lib/table/types.ts +108 -0
  362. package/src/lib/text-area/TextArea.tsx +183 -0
  363. package/src/lib/text-area/text-area.css +156 -0
  364. package/src/lib/text-field/TextField.tsx +218 -0
  365. package/src/lib/text-field/index.ts +8 -0
  366. package/src/lib/text-field/text-field.css +201 -0
  367. package/src/lib/tooltip/Tooltip.tsx +24 -0
  368. package/src/lib/tooltip/tooltip.css +17 -0
  369. package/src/localization/index.ts +47 -0
  370. package/src/main.css +343 -0
  371. package/src/pages/Auth.tsx +848 -0
  372. package/src/pages/Editor.tsx +883 -0
  373. package/src/pages/ErrorPage.tsx +179 -0
  374. package/src/pages/Gallery.tsx +1693 -0
  375. package/src/pages/NewPaymentMethodCallback.tsx +53 -0
  376. package/src/pages/NotFoundPage.tsx +126 -0
  377. package/src/pages/PricingPlans.tsx +155 -0
  378. package/src/pages/auth.css +304 -0
  379. package/src/pages/gallery.css +421 -0
  380. package/src/payments/index.ts +187 -0
  381. package/src/popup-notification/index.ts +90 -0
  382. package/src/services/database/index.ts +1 -0
  383. package/src/services/database/utils.ts +1301 -0
  384. package/src/services/editor/CanvasElement.tsx +2934 -0
  385. package/src/services/editor/CanvasElementConnectionDeleteButton.ts +204 -0
  386. package/src/services/editor/CanvasPopup.tsx +749 -0
  387. package/src/services/editor/EditorService.ts +8157 -0
  388. package/src/services/editor/area.ts +1312 -0
  389. package/src/services/editor/connections.ts +1019 -0
  390. package/src/services/editor/create/condition.ts +25 -0
  391. package/src/services/editor/create/definition-entity.ts +29 -0
  392. package/src/services/editor/create/function-call.ts +25 -0
  393. package/src/services/editor/create/global-event.ts +33 -0
  394. package/src/services/editor/create/loop.ts +25 -0
  395. package/src/services/editor/create/operation.ts +30 -0
  396. package/src/services/editor/create/utils.ts +140 -0
  397. package/src/services/editor/create/variable-declaration.ts +135 -0
  398. package/src/services/editor/create/variable-instance.ts +100 -0
  399. package/src/services/editor/editor-ui-extensions-context.ts +43 -0
  400. package/src/services/editor/entities-metadata.json +9310 -0
  401. package/src/services/editor/icons.ts +1093 -0
  402. package/src/services/editor/index.ts +1 -0
  403. package/src/services/editor/layout.ts +102 -0
  404. package/src/services/editor/modules/built-in-function-implementations/base.ts +14 -0
  405. package/src/services/editor/modules/built-in-function-implementations/create-persisted-entity/index.ts +56 -0
  406. package/src/services/editor/modules/built-in-function-implementations/delete-persisted-entity/index.ts +55 -0
  407. package/src/services/editor/modules/built-in-function-implementations/index.ts +4 -0
  408. package/src/services/editor/modules/built-in-function-implementations/update-persisted-entity/index.ts +56 -0
  409. package/src/services/editor/modules/operations-implementations/external-integrations/google-drive/get-files.ts +183 -0
  410. package/src/services/editor/modules/operations-implementations/external-integrations/google-drive/list-drives.ts +124 -0
  411. package/src/services/editor/modules/operations-implementations/external-integrations/google-drive/list-root-folders.ts +125 -0
  412. package/src/services/editor/modules/operations-implementations/external-integrations/google-drive/smart-fetch-document.ts +702 -0
  413. package/src/services/editor/modules/operations-implementations/external-integrations/google-drive/upload-document.ts +535 -0
  414. package/src/services/editor/modules/operations-implementations/external-integrations/google-gemini/generate-content.ts +193 -0
  415. package/src/services/editor/modules/operations-implementations/external-integrations/google-mail/get-emails.ts +586 -0
  416. package/src/services/editor/modules/operations-implementations/external-integrations/google-mail/send-email.ts +386 -0
  417. package/src/services/editor/modules/operations-implementations/external-integrations/index.ts +12 -0
  418. package/src/services/editor/modules/operations-implementations/external-integrations/slack/channels.ts +240 -0
  419. package/src/services/editor/modules/operations-implementations/external-integrations/slack/messages.ts +210 -0
  420. package/src/services/editor/modules/operations-implementations/external-integrations/slack/replies.ts +200 -0
  421. package/src/services/editor/modules/operations-implementations/external-integrations/slack/send-message.ts +177 -0
  422. package/src/services/editor/modules/operations-implementations/index.ts +1 -0
  423. package/src/services/editor/modules/search-node-implementation/index.ts +42 -0
  424. package/src/services/editor/modules/sql-migrations-generation.tsx +1054 -0
  425. package/src/services/editor/publication/publication.ts +578 -0
  426. package/src/services/editor/ui.ts +1348 -0
  427. package/src/services/editor/utils.ts +5868 -0
  428. package/src/services/editor/value-store.ts +619 -0
  429. package/src/services/execution/built-in-function-implementations.ts +422 -0
  430. package/src/services/execution/index.ts +4747 -0
  431. package/src/services/execution/logic.ts +121 -0
  432. package/src/services/execution/test-instance.tsx +2296 -0
  433. package/src/services/execution/utils.ts +33 -0
  434. package/src/services/execution/value-resolution.test.ts +424 -0
  435. package/src/services/execution/value-resolution.ts +4087 -0
  436. package/src/services/integrations/ExternalIntegrationsService.ts +439 -0
  437. package/src/services/integrations/api.ts +175 -0
  438. package/src/services/local-relational-database/idb_helper.ts +66 -0
  439. package/src/services/local-relational-database/index.ts +3308 -0
  440. package/src/services/local-relational-database/utils.ts +403 -0
  441. package/src/services/notifications/index.ts +525 -0
  442. package/src/services/user/index.ts +144 -0
  443. package/src/setupTests.ts +1 -0
  444. package/src/socket/socket.ts +248 -0
  445. package/src/socket/utils.ts +10 -0
  446. package/src/store/workspace.ts +12 -0
  447. package/src/theme.ts +19 -0
  448. package/src/utils/DOM.ts +39 -0
  449. package/src/utils/date.ts +169 -0
  450. package/src/utils/index.ts +158 -0
  451. package/src/utils/react.tsx +679 -0
  452. package/src/utils/testing.ts +103 -0
@@ -0,0 +1,2395 @@
1
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import {
3
+ BaseEntityNames,
4
+ CanvasEntityConnectionDisabledReason,
5
+ CanvasEntityDisabledReason,
6
+ CanvasEntityOtherDisabledReason,
7
+ CanvasEntityState,
8
+ ChangeSet,
9
+ checkHasBaseEntity,
10
+ checkIsLiteralValueEmpty,
11
+ checkIsLiteralValueExplicitlyEmpty,
12
+ checkIsNullable,
13
+ ENTITY_WITH_DEFAULT_VALUE_TYPES,
14
+ EntityId,
15
+ EntityType,
16
+ getIsInteractiveOrTrue,
17
+ getParentCanvasEntity,
18
+ InputMapState,
19
+ isObject,
20
+ IValueStoreClient,
21
+ OutputMapState,
22
+ PrimitiveTypes,
23
+ ProjectState,
24
+ resolveDataTypeLabel,
25
+ StateMutationAction,
26
+ TERMINATION_TYPES,
27
+ ValueAsTypeState,
28
+ VariableDeclarationState,
29
+ VariableInstanceState,
30
+ } from '@elyx-code/project-logic-tree';
31
+ import { AbsoluteCanvasObject, RelativeObject } from '../../lib/canvas';
32
+ import './dynamic-value.css';
33
+ import { popupNotification } from '../../popup-notification';
34
+ import WarningIcon from '../../assets/warning-sign-24px.png';
35
+ import {
36
+ EntityWithValueState,
37
+ PropertyState,
38
+ ValueDescriptorState,
39
+ } from '@elyx-code/project-logic-tree';
40
+ import { EditorService } from '../../services/editor';
41
+ import {
42
+ DynamicValueTypes,
43
+ IDynamicValue,
44
+ IValueInheritanceLink,
45
+ } from '../../services/editor/value-store';
46
+ import {
47
+ handleLiteralValueChange,
48
+ IValueResolutionContext,
49
+ resolveValue,
50
+ } from '../../services/execution/value-resolution';
51
+ import { LiteralValueState } from '@elyx-code/project-logic-tree';
52
+ import { resolveEntityName } from '@elyx-code/project-logic-tree';
53
+ import CanvasPopupBaseComponent from '../../lib/popup/CanvasPopupBaseComponent';
54
+ import CanvasPopup, { useCanvasPopup } from '../../services/editor/CanvasPopup';
55
+ import { Tooltip } from '@mui/material';
56
+ import { Logger } from '@elyx-code/common-ts-utils';
57
+ import CanvasDropdownMenuCard from '../../lib/dropdown/CanvasDropdownMenuCard';
58
+ import { getCanvasEntityDraggableContainerDOMId } from '../../services/editor/ui';
59
+ import DropdownMenuPopup from '../../lib/dropdown/DropdownMenuPopup';
60
+ import { debounce } from '../../utils';
61
+
62
+ export interface IDynamicValueDefinition {
63
+ value: LiteralValueState;
64
+ source: EntityWithValueState;
65
+ }
66
+
67
+ const renderEscapedString = (str?: string | null, isStringValue?: boolean) => {
68
+ if (!str && str !== '') return str;
69
+ if (typeof str !== 'string') return str;
70
+
71
+ // We split by literal \n, \t, \r, \\ to highlight them.
72
+ const parts = str.split(/(\\n|\\t|\\r|\\\\)/g);
73
+
74
+ const content = parts.map((part, index) => {
75
+ if (part === '\\n' || part === '\\t' || part === '\\r' || part === '\\\\') {
76
+ return (
77
+ <span key={index} style={{ color: '#ffb86c', fontWeight: 'bold' }}>
78
+ {part}
79
+ </span>
80
+ );
81
+ }
82
+ // Render physical newlines if there are any
83
+ if (part.includes('\n')) {
84
+ return part.split('\n').map((line, i, arr) => (
85
+ <React.Fragment key={`${index}-${i}`}>
86
+ {line}
87
+ {i < arr.length - 1 && <br />}
88
+ </React.Fragment>
89
+ ));
90
+ }
91
+ return part;
92
+ });
93
+
94
+ if (isStringValue) {
95
+ return (
96
+ <span style={{ display: 'inline-flex', alignItems: 'center' }}>
97
+ <span style={{ color: 'var(--color-string--value)', filter: 'brightness(0.6)', fontWeight: 800 }}>"</span>
98
+ {content}
99
+ <span style={{ color: 'var(--color-string--value)', filter: 'brightness(0.6)', fontWeight: 800 }}>"</span>
100
+ </span>
101
+ );
102
+ }
103
+
104
+ return content;
105
+ };
106
+
107
+ interface IDynamicValueProps {
108
+ // This is the entity that owns the value, for example an argument of a function call
109
+ valueOwner: EntityWithValueState | PropertyState | ValueDescriptorState;
110
+ // This is the value itself, sometimes it can be passed directly from the outside if it was already calculated
111
+ value?: LiteralValueState;
112
+ valueType?: DynamicValueTypes;
113
+ onlyDefault?: boolean;
114
+ parentCanvasObject?: AbsoluteCanvasObject | RelativeObject;
115
+ project: EditorService;
116
+ parentDisabledReasons: (
117
+ | CanvasEntityDisabledReason
118
+ | CanvasEntityOtherDisabledReason
119
+ )[];
120
+ onEdit?: (
121
+ e: any,
122
+ type: DynamicValueTypes.DefaultValue | DynamicValueTypes.Testing | null,
123
+ ) => void;
124
+ onDelete?: (dynamicValue: IDynamicValue, e: any) => void;
125
+ canDelete?: boolean;
126
+ idSuffix?: string;
127
+ popupPlacement?:
128
+ | 'left'
129
+ | 'right'
130
+ | 'top'
131
+ | 'bottom'
132
+ | 'bottom-end'
133
+ | 'bottom-start'
134
+ | 'left-end'
135
+ | 'left-start'
136
+ | 'right-end'
137
+ | 'right-start'
138
+ | 'top-end'
139
+ | 'top-start';
140
+ }
141
+
142
+ function stringifyValue(value: LiteralValueState): string {
143
+ if (value?.valueAsType) {
144
+ if (Array.isArray(value.valueAsType)) {
145
+ if (value.valueAsType.length === 0) {
146
+ return 'Empty list of entity references';
147
+ }
148
+ return (
149
+ 'List of references to ' +
150
+ value.valueAsType
151
+ .map((entity) => '"' + resolveEntityName(entity, value.project) + '"')
152
+ .join(', ') +
153
+ ' entities'
154
+ );
155
+ } else {
156
+ return (
157
+ 'Reference to "' +
158
+ resolveEntityName(value.valueAsType, value.project) +
159
+ '" entity'
160
+ );
161
+ }
162
+ }
163
+
164
+ const rawValue = value?.value;
165
+
166
+ if (isObject(rawValue)) {
167
+ return JSON.stringify(rawValue, null, 2);
168
+ }
169
+
170
+ if (Array.isArray(rawValue)) {
171
+ return JSON.stringify(rawValue, null, 2);
172
+ }
173
+
174
+ if (rawValue === undefined) {
175
+ return 'Empty';
176
+ }
177
+
178
+ if (rawValue === null) {
179
+ return 'Empty';
180
+ }
181
+
182
+ return rawValue.toString();
183
+ }
184
+
185
+ // Determine if the value owner can have test values
186
+ export function isTestableValue(valueOwner: EntityWithValueState): boolean {
187
+ if (!ENTITY_WITH_DEFAULT_VALUE_TYPES.includes(valueOwner.type)) {
188
+ return false;
189
+ }
190
+
191
+ const dataType = valueOwner.getDataType(null);
192
+
193
+ if (!dataType || !dataType.isResolved) {
194
+ return true;
195
+ } else if (dataType.entity?.type === EntityType.PrimitiveEntity) {
196
+ return true;
197
+ }
198
+
199
+ if (dataType.entity?.type === EntityType.DefinitionEntity) {
200
+ if (
201
+ checkHasBaseEntity(
202
+ dataType.entity,
203
+ BaseEntityNames.EXTERNAL_INTEGRATION_CONNECTION,
204
+ )
205
+ ) {
206
+ return false;
207
+ }
208
+ } else if (dataType.entity?.type === EntityType.BuiltInBaseEntity) {
209
+ if (
210
+ dataType.entity.id.startsWith('BUILT_IN_EXTERNAL_INTEGRATION_CONNECTION_')
211
+ ) {
212
+ return false;
213
+ }
214
+ }
215
+
216
+ return true;
217
+ }
218
+
219
+ export default function DynamicValue(props: IDynamicValueProps) {
220
+ const [updateKey, setValue] = useState(0);
221
+ // For the non-canvas popup dropdown menu
222
+ const [dropdownOpen, setDropdownOpen] = useState(false);
223
+
224
+ const useForceUpdate = () => {
225
+ return () => setValue((value) => value + 1);
226
+ };
227
+ const forceUpdate = useForceUpdate(); // Update the component
228
+
229
+ const onlyDefault = props.onlyDefault || false;
230
+
231
+ const [instanceId] = useState(() => Math.random().toString(36).substring(2, 9));
232
+
233
+ const stable_id = props.valueOwner
234
+ ? 'dynamic-value--' + props.valueOwner.id + (props.idSuffix || '') + '--' + instanceId
235
+ : null;
236
+
237
+ const id: string =
238
+ stable_id ||
239
+ 'temp-value-id--' +
240
+ instanceId +
241
+ (props.idSuffix || '');
242
+
243
+ /*
244
+ The value label color looks different based on the type of the value.
245
+
246
+ If the value is given by the user, we use the primary color.
247
+
248
+ If the value is inherited from a value given by the user higher up in the call stack,
249
+ or if the value is a default value,
250
+ or if the value is empty but it isn't required,
251
+ or if the value is auto- calculated from a simple operation,
252
+ we use the grey color.
253
+
254
+ If the value is the result of a test execution, we use the success color.
255
+
256
+ If the value is empty and it is required, we use the error color.
257
+ */
258
+ const resolveValueMetadata = (
259
+ value: IDynamicValue,
260
+ ): {
261
+ color: 'primary' | 'error' | 'grey' | 'success';
262
+ hint: DynamicValueTypes;
263
+ } => {
264
+ if (!value) {
265
+ return {
266
+ color: 'grey',
267
+ hint: null,
268
+ };
269
+ }
270
+
271
+ switch (value.type) {
272
+ case DynamicValueTypes.Inherited:
273
+ case DynamicValueTypes.DefaultValue:
274
+ case DynamicValueTypes.Empty:
275
+ case DynamicValueTypes.QueuedAutocalculation:
276
+ return {
277
+ color: 'grey',
278
+ hint: value.type,
279
+ };
280
+ case DynamicValueTypes.AutoCalculated:
281
+ case DynamicValueTypes.ExecutionResult:
282
+ return {
283
+ color: 'success',
284
+ hint: value.type,
285
+ };
286
+ case DynamicValueTypes.Missing:
287
+ return {
288
+ color: 'error',
289
+ hint: value.type,
290
+ };
291
+ case DynamicValueTypes.Testing:
292
+ return {
293
+ color: 'primary',
294
+ hint: value.type,
295
+ };
296
+ }
297
+
298
+ return {
299
+ color: 'grey',
300
+ hint: value.type,
301
+ };
302
+ };
303
+
304
+ /*
305
+ Some values are not primitive, for example a function call can return an object
306
+ In this case, we need to display the object in a dialog,
307
+ so we need to resolve the label for the value that isn't the explicit value itself
308
+ But a description of the value, like: Object, List, etc.
309
+ */
310
+ const resolveLabelForValue = (
311
+ value: LiteralValueState,
312
+ valueOwner: EntityWithValueState | PropertyState | ValueDescriptorState,
313
+ ): React.ReactNode => {
314
+ if (!value) {
315
+ return 'Inherited';
316
+ }
317
+
318
+ if (checkIsLiteralValueExplicitlyEmpty(value)) {
319
+ return 'Empty';
320
+ }
321
+
322
+ const valueOwnerDataType = valueOwner?.getDataType(null) || null;
323
+
324
+ // Value as type show the entity name
325
+ if (!!value.valueAsTypeSingle) {
326
+ const entityName = resolveEntityName(
327
+ value.valueAsTypeSingle,
328
+ props.project.logic,
329
+ );
330
+
331
+ return (
332
+ <div className='dynamic-value__resolved-value dynamic-value__resolved-value__object'>
333
+ {entityName}
334
+ </div>
335
+ );
336
+ } else if (!!value.valueAsTypeList) {
337
+ if (Array.isArray(value.valueAsTypeList)) {
338
+ return (
339
+ <div className='dynamic-value__resolved-value dynamic-value__resolved-value__list'>
340
+ List
341
+ </div>
342
+ );
343
+ }
344
+ }
345
+
346
+ if (!!valueOwnerDataType) {
347
+ if (valueOwnerDataType.isList) {
348
+ return (
349
+ <div className='dynamic-value__resolved-value dynamic-value__resolved-value__list'>
350
+ List
351
+ </div>
352
+ );
353
+ }
354
+ }
355
+
356
+ if (Array.isArray(value.value)) {
357
+ return (
358
+ <div className='dynamic-value__resolved-value dynamic-value__resolved-value__list'>
359
+ List
360
+ </div>
361
+ );
362
+ }
363
+
364
+ if (value.name === PrimitiveTypes.String) {
365
+ const stringifiedValue = value?.rawString ?? value?.value?.toString();
366
+
367
+ return (
368
+ <div className='dynamic-value__resolved-value dynamic-value__resolved-value__string'>
369
+ {renderEscapedString(stringifiedValue, true)}
370
+ </div>
371
+ );
372
+ }
373
+
374
+ if (value.name === PrimitiveTypes.Number) {
375
+ const stringifiedValue = value?.value?.toString();
376
+
377
+ return (
378
+ <div className='dynamic-value__resolved-value dynamic-value__resolved-value__number'>
379
+ {stringifiedValue}
380
+ </div>
381
+ );
382
+ }
383
+
384
+ if (value.name === PrimitiveTypes.Boolean) {
385
+ const stringifiedValue = value?.value?.toString();
386
+
387
+ return (
388
+ <div className='dynamic-value__resolved-value dynamic-value__resolved-value__boolean'>
389
+ {stringifiedValue}
390
+ </div>
391
+ );
392
+ }
393
+
394
+ if (
395
+ value.name === PrimitiveTypes.Date ||
396
+ value.name === PrimitiveTypes.UUID
397
+ ) {
398
+ const stringifiedValue = value?.value?.toString();
399
+
400
+ return (
401
+ <div className='dynamic-value__resolved-value dynamic-value__resolved-value__string'>
402
+ {stringifiedValue}
403
+ </div>
404
+ );
405
+ }
406
+
407
+ if (value.name === PrimitiveTypes.Bytes) {
408
+ return (
409
+ <div className='dynamic-value__resolved-value dynamic-value__resolved-value__object'>
410
+ Bytes
411
+ </div>
412
+ );
413
+ }
414
+
415
+ if (typeof value.value === 'object' && value.value !== null) {
416
+ return (
417
+ <div className='dynamic-value__resolved-value dynamic-value__resolved-value__object'>
418
+ Structure
419
+ </div>
420
+ );
421
+ }
422
+
423
+ if (typeof value.value === 'string') {
424
+ const stringifiedValue = value.rawString ?? value.value.toString();
425
+
426
+ return (
427
+ <div className='dynamic-value__resolved-value dynamic-value__resolved-value__string'>
428
+ {renderEscapedString(stringifiedValue, true)}
429
+ </div>
430
+ );
431
+ }
432
+
433
+ if (typeof value.value === 'number') {
434
+ return (
435
+ <div className='dynamic-value__resolved-value dynamic-value__resolved-value__number'>
436
+ {value.value.toString()}
437
+ </div>
438
+ );
439
+ }
440
+
441
+ if (typeof value.value === 'boolean') {
442
+ return (
443
+ <div className='dynamic-value__resolved-value dynamic-value__resolved-value__boolean'>
444
+ {value.value.toString()}
445
+ </div>
446
+ );
447
+ }
448
+
449
+ return value?.value?.toString();
450
+ };
451
+
452
+ const valueOwner = props.valueOwner;
453
+
454
+ const dynamicValue: IDynamicValue | null = useMemo(() => {
455
+ if (props.project.destroyed) {
456
+ return null;
457
+ }
458
+
459
+ let tmpValue: IDynamicValue = {
460
+ value: props.value,
461
+ type: props.valueType,
462
+ valueOwner: props.valueOwner,
463
+ inheritanceLink: null,
464
+ };
465
+
466
+ if (!props.value && !props.valueType) {
467
+ const dynamicValueResolutionResult = !!valueOwner
468
+ ? resolveValue(valueOwner, props.project, onlyDefault)
469
+ : null;
470
+
471
+ const resolvedValue = !!dynamicValueResolutionResult
472
+ ? {
473
+ ...dynamicValueResolutionResult,
474
+ type: props.valueType || dynamicValueResolutionResult.type,
475
+ value: props.value || dynamicValueResolutionResult.value,
476
+ }
477
+ : tmpValue;
478
+
479
+ return resolvedValue;
480
+ }
481
+
482
+ return tmpValue;
483
+ }, [
484
+ valueOwner,
485
+ props.project,
486
+ onlyDefault,
487
+ props.value,
488
+ props.valueType,
489
+ updateKey,
490
+ ]);
491
+
492
+ const defaultValueOnly = useMemo(() => {
493
+ if (props.project.destroyed) {
494
+ return null;
495
+ }
496
+
497
+ return props.onlyDefault
498
+ ? dynamicValue
499
+ : resolveValue(valueOwner, props.project, true);
500
+ }, [props.onlyDefault, dynamicValue, valueOwner, props.project, updateKey]);
501
+
502
+ const testValueOnly = useMemo(() => {
503
+ if (props.project.destroyed) {
504
+ return null;
505
+ }
506
+
507
+ let forcedNoDefault = null;
508
+
509
+ try {
510
+ const context: IValueResolutionContext = {
511
+ ...(props.project as IValueResolutionContext),
512
+ getValueTypePreference(_valueOwnerId: string) {
513
+ if (!isTestableValue(valueOwner as EntityWithValueState)) {
514
+ return DynamicValueTypes.DefaultValue;
515
+ }
516
+
517
+ return DynamicValueTypes.Testing;
518
+ },
519
+ };
520
+
521
+ forcedNoDefault = resolveValue(valueOwner, context, false);
522
+ } catch (e) {
523
+ Logger.error('Error resolving forced no default value', e, valueOwner);
524
+ }
525
+
526
+ return forcedNoDefault?.type === DynamicValueTypes.Testing
527
+ ? forcedNoDefault
528
+ : null;
529
+ }, [valueOwner, props.project, updateKey]);
530
+
531
+ const resolvedStoredInitialPreference:
532
+ | DynamicValueTypes.DefaultValue
533
+ | DynamicValueTypes.Testing =
534
+ !!props.valueOwner.id &&
535
+ (props.project.getValueTypePreference(props.valueOwner.id) as
536
+ | DynamicValueTypes.DefaultValue
537
+ | DynamicValueTypes.Testing);
538
+
539
+ // Check local storage for user preference for this value
540
+ const [valueTypePreference, setValueTypePreference] = useState<
541
+ DynamicValueTypes.DefaultValue | DynamicValueTypes.Testing
542
+ >(resolvedStoredInitialPreference || DynamicValueTypes.DefaultValue);
543
+
544
+ const handleSetPreference = (
545
+ newPreference: DynamicValueTypes.DefaultValue | DynamicValueTypes.Testing,
546
+ ) => {
547
+ setValueTypePreference(newPreference);
548
+ if (props.valueOwner.id) {
549
+ props.project.setValueTypePreference(props.valueOwner.id, newPreference);
550
+ }
551
+ };
552
+
553
+ let finalResolvedPreference = valueTypePreference;
554
+
555
+ if (
556
+ valueTypePreference === DynamicValueTypes.Testing &&
557
+ dynamicValue?.type !== DynamicValueTypes.Testing
558
+ ) {
559
+ finalResolvedPreference = DynamicValueTypes.DefaultValue;
560
+ } else if (
561
+ valueTypePreference === DynamicValueTypes.DefaultValue &&
562
+ dynamicValue?.type === DynamicValueTypes.Testing
563
+ ) {
564
+ finalResolvedPreference = DynamicValueTypes.Testing;
565
+ }
566
+
567
+ // Check if the counterpart value type exists
568
+ // Meaning, if default is shown, check if testing exists, and vice versa
569
+ let counterpartTypeExists = false;
570
+
571
+ if (
572
+ finalResolvedPreference === DynamicValueTypes.DefaultValue &&
573
+ testValueOnly?.type === DynamicValueTypes.Testing
574
+ ) {
575
+ counterpartTypeExists = true;
576
+ } else if (
577
+ finalResolvedPreference === DynamicValueTypes.Testing &&
578
+ (defaultValueOnly?.type === DynamicValueTypes.DefaultValue ||
579
+ defaultValueOnly?.type === DynamicValueTypes.External)
580
+ ) {
581
+ counterpartTypeExists = true;
582
+ }
583
+
584
+ const canSwitch =
585
+ !props.parentDisabledReasons.length &&
586
+ !props.onlyDefault &&
587
+ ENTITY_WITH_DEFAULT_VALUE_TYPES.includes(valueOwner?.type) &&
588
+ // Should not allow switching if the current value is default, and the value owner is not interactive
589
+ !(
590
+ finalResolvedPreference === DynamicValueTypes.DefaultValue &&
591
+ !getIsInteractiveOrTrue(valueOwner)
592
+ ) &&
593
+ isTestableValue(valueOwner as EntityWithValueState);
594
+
595
+ const valueMetadata = resolveValueMetadata(dynamicValue);
596
+ const thereIsValue = !checkIsLiteralValueEmpty(dynamicValue?.value);
597
+
598
+ const canCopyValue = thereIsValue;
599
+
600
+ const canDelete =
601
+ (props.canDelete ?? true) &&
602
+ !!props.onDelete &&
603
+ valueOwner.type !== EntityType.OutputMap &&
604
+ !props.parentDisabledReasons.length;
605
+
606
+ const valueOwnerDataType = valueOwner?.getDataType(null) || null;
607
+
608
+ const relevantParentDisabledReasons = props.parentDisabledReasons.filter(
609
+ (reason) => {
610
+ if (
611
+ dynamicValue?.type === DynamicValueTypes.ExecutionResult &&
612
+ [
613
+ CanvasEntityConnectionDisabledReason.CannotInteractWithInputInVariableWithInternalCalls,
614
+ CanvasEntityConnectionDisabledReason.PropertyIsConstantItsValueCantBeReset,
615
+ ].includes(reason as any)
616
+ ) {
617
+ return false;
618
+ }
619
+
620
+ return true;
621
+ },
622
+ );
623
+
624
+ const canEdit =
625
+ (!!dynamicValue?.type
626
+ ? !(
627
+ [
628
+ DynamicValueTypes.ExecutionResult,
629
+ DynamicValueTypes.AutoCalculated,
630
+ DynamicValueTypes.QueuedAutocalculation,
631
+ ] as string[]
632
+ ).includes(dynamicValue?.type)
633
+ : true) &&
634
+ !!props.onEdit &&
635
+ !props.parentDisabledReasons.length;
636
+
637
+ const canSetToNull = canEdit && checkIsNullable(valueOwnerDataType);
638
+
639
+ const memoizedCanvasPopupBody = useCallback(
640
+ () => (
641
+ <CanvasPopupBaseComponent
642
+ containerClasses={[
643
+ 'tooltip',
644
+ 'tooltip--large-font',
645
+ 'tooltip--dynamic-value',
646
+ ]}
647
+ id={id + '--popup--body'}
648
+ >
649
+ <h3 style={{ marginTop: 0, marginBottom: 5 }}>
650
+ {dynamicValue?.type === DynamicValueTypes.Testing && 'Testing value'}
651
+ {dynamicValue?.type === DynamicValueTypes.DefaultValue &&
652
+ 'Default value'}
653
+ {dynamicValue?.type === DynamicValueTypes.AutoCalculated &&
654
+ 'Auto-calculated value'}
655
+ {dynamicValue?.type === DynamicValueTypes.ExecutionResult &&
656
+ 'Execution result value'}
657
+ {dynamicValue?.type === DynamicValueTypes.Inherited &&
658
+ 'Inherited value'}
659
+ {dynamicValue?.type === DynamicValueTypes.Missing &&
660
+ 'Missing required value'}
661
+ </h3>
662
+ {canEdit ? (
663
+ <>
664
+ Double click to <b>edit</b>
665
+ </>
666
+ ) : (
667
+ <>
668
+ Double click to <b>inspect</b>
669
+ </>
670
+ )}
671
+ <br />
672
+ <br />
673
+
674
+ {/* Data type label */}
675
+ <b>Type: </b>
676
+ {resolveDataTypeLabel(valueOwnerDataType)}
677
+
678
+ <br />
679
+
680
+ {/* horizontal dividing line */}
681
+ <hr />
682
+
683
+ {thereIsValue ? (
684
+ <div className='tooltip--dynamic-value__value'>
685
+ {renderEscapedString(
686
+ dynamicValue.value?.name === PrimitiveTypes.String && dynamicValue.value?.rawString !== null
687
+ ? dynamicValue.value.rawString
688
+ : stringifyValue(dynamicValue.value),
689
+ dynamicValue.value?.name === PrimitiveTypes.String
690
+ )}
691
+ </div>
692
+ ) : (
693
+ <span className='dynamic-value__empty'>There is no value</span>
694
+ )}
695
+ </CanvasPopupBaseComponent>
696
+ ),
697
+ [
698
+ id,
699
+ dynamicValue?.value?.value,
700
+ dynamicValue?.value?.valueAsType,
701
+ canEdit,
702
+ thereIsValue,
703
+ valueOwnerDataType,
704
+ updateKey,
705
+ ],
706
+ );
707
+
708
+ // Only if there is a 'parentCanvasObject' prop, we should use the canvasPopup
709
+ // Otherwise, we should use the Tooltip component
710
+ const canvasPopup = !!props.parentCanvasObject
711
+ ? useCanvasPopup(
712
+ {
713
+ editor: props.project,
714
+ id: id + '--popup',
715
+ anchorQuerySelector: '#' + id,
716
+ arrowClasses: ['dynamic-value-popup-arrow'],
717
+ parentCanvasObject: props.parentCanvasObject,
718
+ arrowContainerSelector: '#' + id + '--popup--body',
719
+ placement: props.popupPlacement || 'bottom',
720
+ },
721
+ memoizedCanvasPopupBody,
722
+ [
723
+ updateKey,
724
+ dynamicValue?.value?.value,
725
+ dynamicValue?.value?.valueAsType,
726
+ ],
727
+ )
728
+ .setupHover({
729
+ closeDelay: 80,
730
+ openDelay: 600,
731
+ })
732
+ .onBeforeShow((e) => {
733
+ const otherPopup =
734
+ CanvasPopup.globalCanvasPopups[
735
+ id + '--popup-dynamic-value-switch-button'
736
+ ];
737
+
738
+ if (otherPopup && otherPopup.open) {
739
+ return false;
740
+ }
741
+
742
+ if (props.parentDisabledReasons.length) {
743
+ return false;
744
+ }
745
+
746
+ return true;
747
+ })
748
+ : null;
749
+
750
+ const memoizedCanvasPopupSwitchButtonBody = useCallback(
751
+ () => (
752
+ <CanvasPopupBaseComponent
753
+ containerClasses={[
754
+ 'tooltip',
755
+ 'tooltip--large-font',
756
+ 'tooltip--dynamic-value',
757
+ ]}
758
+ id={id + '--popup-dynamic-value-switch-button--body'}
759
+ >
760
+ {!counterpartTypeExists ? (
761
+ <>
762
+ Click to add new{' '}
763
+ <b>
764
+ {dynamicValue?.type === DynamicValueTypes.DefaultValue ||
765
+ dynamicValue?.type === DynamicValueTypes.External ||
766
+ dynamicValue?.type === DynamicValueTypes.Empty
767
+ ? 'test'
768
+ : 'default'}{' '}
769
+ value
770
+ </b>
771
+ </>
772
+ ) : (
773
+ <>
774
+ Click to switch to{' '}
775
+ <b>
776
+ {dynamicValue?.type === DynamicValueTypes.DefaultValue ||
777
+ dynamicValue?.type === DynamicValueTypes.External ||
778
+ dynamicValue?.type === DynamicValueTypes.Empty
779
+ ? 'test'
780
+ : 'default'}{' '}
781
+ value
782
+ </b>
783
+ </>
784
+ )}
785
+ </CanvasPopupBaseComponent>
786
+ ),
787
+ [
788
+ id,
789
+ dynamicValue?.type,
790
+ dynamicValue?.value?.value,
791
+ dynamicValue?.value?.valueAsType,
792
+ counterpartTypeExists,
793
+ updateKey,
794
+ ],
795
+ );
796
+
797
+ const canvasPopupSwitchButton = !!props.parentCanvasObject
798
+ ? useCanvasPopup(
799
+ {
800
+ editor: props.project,
801
+ id: id + '--popup-dynamic-value-switch-button',
802
+ anchorQuerySelector: `#switch-dynamic-value-type-button--${id}`,
803
+ // arrowClasses: ['dropdown-menu-card-arrow'],
804
+ parentCanvasObject: props.parentCanvasObject,
805
+ arrowContainerSelector:
806
+ '#' + id + '--popup-dynamic-value-switch-button--body',
807
+ placement: props.popupPlacement === 'bottom' ? 'top' : 'bottom',
808
+ persisted: true,
809
+ offset: -6,
810
+ },
811
+ memoizedCanvasPopupSwitchButtonBody,
812
+ [
813
+ updateKey,
814
+ dynamicValue?.value?.value,
815
+ dynamicValue?.value?.valueAsType,
816
+ ],
817
+ )
818
+ .setupHover({
819
+ closeDelay: 80,
820
+ openDelay: 600,
821
+ })
822
+ .onBeforeShow((e, self) => {
823
+ e.preventDefault();
824
+ e.stopImmediatePropagation();
825
+ e.stopPropagation();
826
+
827
+ if (props.parentDisabledReasons.length) {
828
+ return false;
829
+ }
830
+
831
+ return true;
832
+ })
833
+ : null;
834
+
835
+ const debouncedCopyAndNotify = debounce(
836
+ function () {
837
+ if (!canCopyValue) {
838
+ return;
839
+ }
840
+
841
+ let stringifiedValue = '';
842
+
843
+ try {
844
+ stringifiedValue = stringifyValue(dynamicValue?.value);
845
+ } catch (e) {
846
+ Logger.error('Error stringifying value for copy', e, dynamicValue);
847
+ stringifiedValue = '';
848
+ }
849
+
850
+ // Copy to clipboard
851
+ navigator.clipboard.writeText(stringifiedValue);
852
+
853
+ // Show toast notification
854
+ popupNotification({
855
+ text: 'Value copied to clipboard',
856
+ // color: 'success',
857
+ });
858
+ },
859
+ 400,
860
+ true,
861
+ );
862
+
863
+ const _handleLiteralValueChange = (
864
+ newValue: string | number | boolean | string[],
865
+ localOnlyDefault?: boolean,
866
+ actionName?: StateMutationAction,
867
+ ) => {
868
+ let storage: ChangeSet | IValueStoreClient = props.project.localTestValues;
869
+
870
+ if (localOnlyDefault ?? onlyDefault) {
871
+ storage = props.project.logic.addChangeSet(
872
+ new ChangeSet(
873
+ props.project.logic,
874
+ ProjectState.sessionAuthor,
875
+ new Date().toISOString(),
876
+ valueOwner,
877
+ false,
878
+ actionName || StateMutationAction.RemoveLiteralValue,
879
+ ),
880
+ );
881
+ }
882
+
883
+ const changeSet = handleLiteralValueChange(
884
+ newValue,
885
+ valueOwner,
886
+ props.project.logic,
887
+ storage,
888
+ );
889
+
890
+ if (!!changeSet) {
891
+ props.project.renderAndCloseChangeSet(changeSet);
892
+ } else {
893
+ props.project.emit(valueOwner.id, {});
894
+ }
895
+
896
+ forceUpdate();
897
+ };
898
+
899
+ const memoizedCanvasMenuDropdownBody = useCallback(
900
+ (popup: CanvasPopup) => (
901
+ <CanvasPopupBaseComponent
902
+ id={'canvas-dropdown-menu--' + id + '--popup--body'}
903
+ >
904
+ <CanvasDropdownMenuCard
905
+ options={[
906
+ {
907
+ project: props.project,
908
+ onClick: async (_, e) => {
909
+ if (canEdit) {
910
+ let nextValueType = DynamicValueTypes.DefaultValue;
911
+ handleSetPreference(nextValueType);
912
+
913
+ props.onEdit?.(e, nextValueType);
914
+ }
915
+ },
916
+ label: 'Edit',
917
+ iconEnd: 'fa-solid fa-pencil',
918
+ disabled: !canEdit,
919
+ tooltip: !canEdit
920
+ ? 'Cannot edit this value'
921
+ : 'Edit the value (you can also double click the value directly)',
922
+ parentCanvasObject: popup.self,
923
+ id: 'canvas-dropdown-menu--' + id + '--edit',
924
+ },
925
+ ...(() => {
926
+ if (!!valueOwnerDataType && valueOwnerDataType.asType) {
927
+ const asTypeValue = dynamicValue?.value?.valueAsType;
928
+ const thereAreMultipleEntityReferences =
929
+ Array.isArray(asTypeValue) && asTypeValue.length > 1;
930
+ const isEmpty =
931
+ !asTypeValue ||
932
+ (Array.isArray(asTypeValue) && asTypeValue.length === 0);
933
+
934
+ let target: ValueAsTypeState | null =
935
+ asTypeValue as ValueAsTypeState;
936
+
937
+ if (thereAreMultipleEntityReferences) {
938
+ target = (asTypeValue as ValueAsTypeState[])[0];
939
+ }
940
+
941
+ let definitionName = '';
942
+
943
+ let canvasEntity: CanvasEntityState | null = null;
944
+
945
+ if (target) {
946
+ definitionName = resolveEntityName(
947
+ target,
948
+ props.project.logic,
949
+ );
950
+
951
+ canvasEntity = getParentCanvasEntity(target);
952
+ }
953
+
954
+ return [
955
+ {
956
+ project: props.project,
957
+ onClick: async (
958
+ _: any,
959
+ e: React.MouseEvent<
960
+ HTMLButtonElement | HTMLAnchorElement,
961
+ MouseEvent
962
+ >,
963
+ ) => {
964
+ e.stopPropagation();
965
+
966
+ // This stops the native DOM bubbling that the Canvas Engine is listening to
967
+ e.nativeEvent.stopImmediatePropagation();
968
+ e.nativeEvent.stopPropagation();
969
+
970
+ if (isEmpty) {
971
+ return;
972
+ }
973
+
974
+ if (!canvasEntity) {
975
+ return;
976
+ }
977
+
978
+ const objectId =
979
+ getCanvasEntityDraggableContainerDOMId(canvasEntity);
980
+
981
+ props.project?.canvas?.scrollToObjectById(objectId);
982
+ },
983
+ label: thereAreMultipleEntityReferences
984
+ ? 'Go to first definition'
985
+ : 'Go to definition',
986
+ iconEnd: 'fa-solid fa-arrow-up-right-from-square',
987
+ disabled: isEmpty || !canvasEntity,
988
+ tooltip: isEmpty
989
+ ? 'No definition has been set'
990
+ : !canvasEntity
991
+ ? 'Definition is not in the canvas'
992
+ : thereAreMultipleEntityReferences
993
+ ? `Go to the first definition of this value (${definitionName})`
994
+ : `Go to ${definitionName} in the canvas`,
995
+ parentCanvasObject: popup.self,
996
+ id: 'canvas-dropdown-menu--' + id + '--go-to-definition',
997
+ },
998
+ ];
999
+ }
1000
+
1001
+ return [];
1002
+ })(),
1003
+ {
1004
+ project: props.project,
1005
+ onClick: async (_, e) => {
1006
+ if (canDelete) {
1007
+ props.onDelete?.(dynamicValue, e);
1008
+ }
1009
+ },
1010
+ label: 'Delete',
1011
+ color: 'error',
1012
+ iconEnd: 'fa-solid fa-trash-can',
1013
+ disabled: !canDelete,
1014
+ tooltip: !canDelete
1015
+ ? 'Cannot delete this value'
1016
+ : 'Remove the value to not override any previous value that might be present',
1017
+ parentCanvasObject: popup.self,
1018
+ id: 'canvas-dropdown-menu--' + id + '--delete',
1019
+ },
1020
+ {
1021
+ project: props.project,
1022
+ onClick: debouncedCopyAndNotify,
1023
+ label: 'Copy value',
1024
+ color: 'primary',
1025
+ iconEnd: 'fa-regular fa-copy',
1026
+ disabled: !canCopyValue,
1027
+ tooltip: !canCopyValue ? 'Cannot copy this value' : '',
1028
+ parentCanvasObject: popup.self,
1029
+ id: 'canvas-dropdown-menu--' + id + '--copy',
1030
+ },
1031
+ {
1032
+ project: props.project,
1033
+ onClick: async () => {
1034
+ if (!canSetToNull) {
1035
+ return;
1036
+ }
1037
+
1038
+ const localOnlyDefault =
1039
+ finalResolvedPreference === DynamicValueTypes.DefaultValue ||
1040
+ onlyDefault;
1041
+
1042
+ _handleLiteralValueChange(
1043
+ null,
1044
+ localOnlyDefault,
1045
+ StateMutationAction.SetLiteralValueToExplicitlyEmpty,
1046
+ );
1047
+ },
1048
+ label: 'Set as empty',
1049
+ color: 'primary',
1050
+ iconEnd: 'fa-solid fa-empty-set',
1051
+ disabled: !canSetToNull,
1052
+ tooltip: !canSetToNull
1053
+ ? 'Cannot set this value as empty' +
1054
+ (!checkIsNullable(valueOwnerDataType)
1055
+ ? '. The data type doesn not support empty values'
1056
+ : '')
1057
+ : 'Sets a value explicitly as empty (null), to override any previous value that might be present',
1058
+ parentCanvasObject: popup.self,
1059
+ id: 'canvas-dropdown-menu--' + id + '--set-empty',
1060
+ },
1061
+ {
1062
+ project: props.project,
1063
+ onClick: async (_, e) => {
1064
+ if (canSwitch) {
1065
+ let nextValueType = finalResolvedPreference as
1066
+ | DynamicValueTypes.DefaultValue
1067
+ | DynamicValueTypes.Testing;
1068
+
1069
+ // if (
1070
+ // dynamicValue?.type === DynamicValueTypes.Inherited &&
1071
+ // valueOwner.type === EntityType.InputMap
1072
+ // ) {
1073
+ // finalResolvedPreference === DynamicValueTypes.DefaultValue;
1074
+ // } else if (
1075
+ if (
1076
+ finalResolvedPreference === DynamicValueTypes.DefaultValue
1077
+ ) {
1078
+ nextValueType = DynamicValueTypes.Testing;
1079
+ } else if (
1080
+ finalResolvedPreference === DynamicValueTypes.Testing
1081
+ ) {
1082
+ nextValueType = DynamicValueTypes.DefaultValue;
1083
+ } else if (dynamicValue?.type === DynamicValueTypes.Testing) {
1084
+ nextValueType = DynamicValueTypes.DefaultValue;
1085
+ } else {
1086
+ // If current value is neither default nor testing, switch to the other type
1087
+ nextValueType = DynamicValueTypes.Testing;
1088
+ }
1089
+
1090
+ handleSetPreference(nextValueType);
1091
+
1092
+ // if (
1093
+ // dynamicValue?.type === DynamicValueTypes.Inherited &&
1094
+ // valueOwner.type === EntityType.InputMap
1095
+ // ) {
1096
+ // props.onEdit?.(e, nextValueType);
1097
+ // } else if (!counterpartTypeExists) {
1098
+ if (!counterpartTypeExists) {
1099
+ props.onEdit?.(e, nextValueType);
1100
+ }
1101
+ }
1102
+ },
1103
+ label: (() => {
1104
+ if (
1105
+ dynamicValue.type === DynamicValueTypes.Testing &&
1106
+ canSwitch
1107
+ ) {
1108
+ if (!counterpartTypeExists) {
1109
+ return 'Click to add real value';
1110
+ } else {
1111
+ return 'Use real value';
1112
+ }
1113
+ }
1114
+
1115
+ if (
1116
+ dynamicValue.type !== DynamicValueTypes.Testing &&
1117
+ canSwitch
1118
+ ) {
1119
+ if (!counterpartTypeExists) {
1120
+ return 'Click to add test value';
1121
+ } else {
1122
+ return 'Use test value';
1123
+ }
1124
+ }
1125
+
1126
+ return 'Switch values';
1127
+ })(),
1128
+ color: 'primary',
1129
+ iconEnd:
1130
+ 'fa-solid fa-' + (counterpartTypeExists ? 'retweet' : 'plus'),
1131
+ disabled: (() => {
1132
+ if (
1133
+ dynamicValue.type === DynamicValueTypes.Testing &&
1134
+ canSwitch
1135
+ ) {
1136
+ return false;
1137
+ }
1138
+
1139
+ if (
1140
+ dynamicValue.type !== DynamicValueTypes.Testing &&
1141
+ canSwitch
1142
+ ) {
1143
+ return false;
1144
+ }
1145
+
1146
+ return true;
1147
+ })(),
1148
+ tooltip: (() => {
1149
+ if (
1150
+ dynamicValue.type === DynamicValueTypes.Testing &&
1151
+ canSwitch
1152
+ ) {
1153
+ return 'Switch the active value to use the real value that will be published with the project';
1154
+ }
1155
+
1156
+ if (
1157
+ dynamicValue.type !== DynamicValueTypes.Testing &&
1158
+ canSwitch
1159
+ ) {
1160
+ return 'Switch the active value to use a temporary value for testing purposes';
1161
+ }
1162
+
1163
+ // Disabled explanations
1164
+
1165
+ return 'Switch active values';
1166
+ })(),
1167
+ parentCanvasObject: popup.self,
1168
+ id: 'canvas-dropdown-menu--' + id + '--switch-values',
1169
+ },
1170
+ ]}
1171
+ />
1172
+ </CanvasPopupBaseComponent>
1173
+ ),
1174
+ [
1175
+ canDelete,
1176
+ canEdit,
1177
+ canCopyValue,
1178
+ canSetToNull,
1179
+ canSwitch,
1180
+ dynamicValue,
1181
+ id,
1182
+ valueOwnerDataType,
1183
+ updateKey,
1184
+ counterpartTypeExists,
1185
+ finalResolvedPreference,
1186
+ debouncedCopyAndNotify,
1187
+ _handleLiteralValueChange,
1188
+ props.onEdit,
1189
+ props.onDelete,
1190
+ props.project,
1191
+ ],
1192
+ );
1193
+
1194
+ const canvasMenuDropdown = !!props.parentCanvasObject
1195
+ ? useCanvasPopup(
1196
+ {
1197
+ editor: props.project,
1198
+ id: 'canvas-dropdown-menu--' + id + '--popup',
1199
+ anchorQuerySelector: `${id}--dropdown-menu-button`,
1200
+ arrowClasses: ['dropdown-menu-card-arrow'],
1201
+ parentCanvasObject: props.parentCanvasObject,
1202
+ arrowContainerSelector:
1203
+ '#canvas-dropdown-menu--' + id + '--popup--body',
1204
+ placement: 'bottom',
1205
+ },
1206
+ memoizedCanvasMenuDropdownBody,
1207
+ [
1208
+ canDelete,
1209
+ canEdit,
1210
+ canCopyValue,
1211
+ canSetToNull,
1212
+ canSwitch,
1213
+ dynamicValue,
1214
+ id,
1215
+ valueOwnerDataType,
1216
+ updateKey,
1217
+ counterpartTypeExists,
1218
+ finalResolvedPreference,
1219
+ debouncedCopyAndNotify,
1220
+ _handleLiteralValueChange,
1221
+ props.onEdit,
1222
+ props.onDelete,
1223
+ props.project,
1224
+ ],
1225
+ )
1226
+ : null;
1227
+
1228
+ // Recursively unfold the inheritance tree and resolve all dependencies so we subscribe them as events
1229
+ const subscribeValueInheritanceUpdates = useCallback(
1230
+ (inheritanceLink: IValueInheritanceLink): (() => void) => {
1231
+ // Create a stable handler
1232
+ const handleInheritanceUpdate = () => {
1233
+ forceUpdate();
1234
+ };
1235
+
1236
+ const cleanupFunctions: (() => void)[] = [];
1237
+
1238
+ // Use a recursive inner function to gather all listeners
1239
+ const subscribeRecursive = (link: IValueInheritanceLink) => {
1240
+ link.sources.forEach((valueInheritanceLink) => {
1241
+ if (!valueInheritanceLink) {
1242
+ return;
1243
+ }
1244
+
1245
+ const ownerId = valueInheritanceLink.valueOwner.id;
1246
+ props.project.on(ownerId, handleInheritanceUpdate);
1247
+ cleanupFunctions.push(() => {
1248
+ props.project.off(ownerId, handleInheritanceUpdate);
1249
+ });
1250
+
1251
+ const dataType = (
1252
+ valueInheritanceLink.valueOwner as VariableDeclarationState
1253
+ ).dataType;
1254
+ if (dataType) {
1255
+ const dataTypeId = dataType.id;
1256
+ props.project.on(dataTypeId, handleInheritanceUpdate);
1257
+ cleanupFunctions.push(() => {
1258
+ props.project.off(dataTypeId, handleInheritanceUpdate);
1259
+ });
1260
+ }
1261
+
1262
+ if (valueInheritanceLink.inheritanceLink) {
1263
+ subscribeRecursive(valueInheritanceLink.inheritanceLink);
1264
+ }
1265
+ });
1266
+ };
1267
+
1268
+ // Start the recursion
1269
+ subscribeRecursive(inheritanceLink);
1270
+
1271
+ // Return a single function that cleans up everything
1272
+ return () => {
1273
+ cleanupFunctions.forEach((cleanup) => cleanup());
1274
+ };
1275
+ },
1276
+ [props.project, forceUpdate], // Dependencies for the useCallback
1277
+ );
1278
+
1279
+ useEffect(() => {
1280
+ const handleValueOwnerUpdate = () => {
1281
+ forceUpdate();
1282
+ };
1283
+
1284
+ const handleValueUpdate = () => {
1285
+ forceUpdate();
1286
+ };
1287
+
1288
+ const handleValueStoresinitialized = () => {
1289
+ forceUpdate();
1290
+ };
1291
+
1292
+ props.project.on(props.valueOwner.id, handleValueOwnerUpdate);
1293
+
1294
+ if (props.value?.id) {
1295
+ props.project.on(props.value.id, handleValueUpdate);
1296
+ }
1297
+
1298
+ if (
1299
+ valueOwner.type === EntityType.InputMap ||
1300
+ valueOwner.type === EntityType.OutputMap
1301
+ ) {
1302
+ let relevantParent = valueOwner.parent;
1303
+
1304
+ if (
1305
+ relevantParent &&
1306
+ relevantParent.type === EntityType.VariableInstance
1307
+ ) {
1308
+ relevantParent = relevantParent.declaration;
1309
+ }
1310
+
1311
+ if (relevantParent) {
1312
+ props.project.on(relevantParent.id, handleValueOwnerUpdate);
1313
+ }
1314
+ }
1315
+
1316
+ // Call the recursive subscription and store its cleanup function
1317
+ let inheritanceCleanup: (() => void) | null = null;
1318
+ if (dynamicValue?.inheritanceLink) {
1319
+ inheritanceCleanup = subscribeValueInheritanceUpdates(
1320
+ dynamicValue.inheritanceLink,
1321
+ );
1322
+ }
1323
+
1324
+ const subs: EntityId[] = (() => {
1325
+ if (!props.valueOwner) {
1326
+ return [];
1327
+ }
1328
+ const entities: Set<string> = new Set(props.valueOwner?.id);
1329
+
1330
+ if (
1331
+ [
1332
+ EntityType.OutputMap,
1333
+ EntityType.InputMap,
1334
+ EntityType.VariableInstance,
1335
+ ].includes(props.valueOwner.type) &&
1336
+ !!(
1337
+ props.valueOwner as
1338
+ | VariableInstanceState
1339
+ | InputMapState
1340
+ | OutputMapState
1341
+ ).declaration?.id
1342
+ ) {
1343
+ entities.add(
1344
+ (
1345
+ props.valueOwner as
1346
+ | VariableInstanceState
1347
+ | InputMapState
1348
+ | OutputMapState
1349
+ ).declaration?.id,
1350
+ );
1351
+ }
1352
+
1353
+ if (
1354
+ [EntityType.OutputMap, EntityType.InputMap].includes(
1355
+ props.valueOwner.type,
1356
+ )
1357
+ ) {
1358
+ const self: InputMapState | OutputMapState = props.valueOwner as
1359
+ | InputMapState
1360
+ | OutputMapState;
1361
+
1362
+ const parentToTarget =
1363
+ self.parent?.type === EntityType.VariableInstance
1364
+ ? self.parent.declaration
1365
+ : self.parent;
1366
+
1367
+ if (
1368
+ [EntityType.VariableDeclaration, ...TERMINATION_TYPES].includes(
1369
+ parentToTarget.type,
1370
+ )
1371
+ ) {
1372
+ entities.add(parentToTarget.id);
1373
+ }
1374
+ }
1375
+
1376
+ return [...entities];
1377
+ })();
1378
+
1379
+ const handleValueChange = (payload: any) => {
1380
+ if (
1381
+ payload?.owner?.id === props.valueOwner?.id ||
1382
+ subs.includes(payload?.owner?.id)
1383
+ ) {
1384
+ forceUpdate();
1385
+ }
1386
+ };
1387
+
1388
+ props.project.on('value-stores-initialized', handleValueStoresinitialized);
1389
+ props.project.on('value-written', handleValueChange);
1390
+ props.project.on('value-removed', handleValueChange);
1391
+ props.project.on('test-setup-started', handleValueChange);
1392
+ props.project.on('test-exited', handleValueChange);
1393
+
1394
+ return () => {
1395
+ props.project.off(props.valueOwner.id, handleValueOwnerUpdate);
1396
+ if (props.value?.id) {
1397
+ props.project.off(props.value.id, handleValueUpdate);
1398
+ }
1399
+
1400
+ if (
1401
+ valueOwner.type === EntityType.InputMap ||
1402
+ valueOwner.type === EntityType.OutputMap
1403
+ ) {
1404
+ let relevantParent = valueOwner.parent;
1405
+
1406
+ if (
1407
+ relevantParent &&
1408
+ relevantParent.type === EntityType.VariableInstance
1409
+ ) {
1410
+ relevantParent = relevantParent.declaration;
1411
+ }
1412
+
1413
+ if (relevantParent) {
1414
+ props.project.off(relevantParent.id, handleValueOwnerUpdate);
1415
+ }
1416
+ }
1417
+
1418
+ // Call the recursive cleanup function
1419
+ if (inheritanceCleanup) {
1420
+ inheritanceCleanup();
1421
+ }
1422
+
1423
+ props.project.off(
1424
+ 'value-stores-initialized',
1425
+ handleValueStoresinitialized,
1426
+ );
1427
+ props.project.off('value-written', handleValueChange);
1428
+ props.project.off('value-removed', handleValueChange);
1429
+ };
1430
+ }, [
1431
+ props.project,
1432
+ props.valueOwner.id,
1433
+ props.value?.id,
1434
+ dynamicValue, // Rerun if the resolved dynamicValue changes
1435
+ subscribeValueInheritanceUpdates, // The stable useCallback function
1436
+ updateKey,
1437
+ ]);
1438
+
1439
+ // Set a flag on the event during the capture phase
1440
+ useEffect(() => {
1441
+ const el = document.getElementById(id);
1442
+ if (!el) return;
1443
+
1444
+ const flagEvent = (e: MouseEvent) => {
1445
+ // We tag the event object directly
1446
+ (e as any).__canvasIgnore = true;
1447
+ };
1448
+
1449
+ // The 'true' argument attaches this to the CAPTURE phase (top-down)
1450
+ el.addEventListener('dblclick', flagEvent, true);
1451
+ el.addEventListener('mousedown', flagEvent, true);
1452
+
1453
+ return () => {
1454
+ el.removeEventListener('dblclick', flagEvent, true);
1455
+ el.removeEventListener('mousedown', flagEvent, true);
1456
+ };
1457
+ }, [id]);
1458
+
1459
+ return (
1460
+ <Tooltip
1461
+ // The styles overrides below can be cleaned up
1462
+ classes={{
1463
+ tooltip: 'tooltip tooltip--dynamic-value',
1464
+ arrow: 'mui-tooltip-arrow',
1465
+ }}
1466
+ slotProps={{
1467
+ popper: {
1468
+ className: 'tooltip tooltip--dynamic-value',
1469
+ style: {
1470
+ margin: 0,
1471
+ padding: 0,
1472
+ },
1473
+ modifiers: [
1474
+ {
1475
+ name: 'offset',
1476
+ options: {
1477
+ offset: [0, 8],
1478
+ },
1479
+ },
1480
+ ],
1481
+ },
1482
+ tooltip: {
1483
+ className: 'tooltip tooltip--dynamic-value',
1484
+ style: {
1485
+ margin: 0,
1486
+ padding: 5,
1487
+ },
1488
+ },
1489
+ arrow: {
1490
+ className: 'mui-tooltip-arrow',
1491
+ },
1492
+ }}
1493
+ arrow
1494
+ title={
1495
+ !props.parentCanvasObject && !props.parentDisabledReasons.length ? (
1496
+ <>
1497
+ <h3 style={{ marginTop: 0, marginBottom: 5 }}>
1498
+ {dynamicValue?.type === DynamicValueTypes.Testing &&
1499
+ 'Testing value'}
1500
+ {dynamicValue?.type === DynamicValueTypes.DefaultValue &&
1501
+ 'Default value'}
1502
+ {dynamicValue?.type === DynamicValueTypes.AutoCalculated &&
1503
+ 'Auto-calculated value'}
1504
+ {dynamicValue?.type === DynamicValueTypes.ExecutionResult &&
1505
+ 'Execution result value'}
1506
+ {dynamicValue?.type === DynamicValueTypes.Inherited &&
1507
+ 'Inherited value'}
1508
+ {dynamicValue?.type === DynamicValueTypes.Missing &&
1509
+ 'Missing required value'}
1510
+ </h3>
1511
+ {canEdit ? (
1512
+ <>
1513
+ Double click to <b>edit</b>
1514
+ </>
1515
+ ) : (
1516
+ <>
1517
+ Double click to <b>inspect</b>
1518
+ </>
1519
+ )}
1520
+ <br />
1521
+
1522
+ {/* horizontal dividing line */}
1523
+ <hr />
1524
+
1525
+ {thereIsValue ? (
1526
+ <div className='tooltip--dynamic-value__value'>
1527
+ {renderEscapedString(
1528
+ dynamicValue.value?.name === PrimitiveTypes.String && dynamicValue.value?.rawString !== null
1529
+ ? dynamicValue.value.rawString
1530
+ : stringifyValue(dynamicValue.value),
1531
+ dynamicValue.value?.name === PrimitiveTypes.String
1532
+ )}
1533
+ </div>
1534
+ ) : (
1535
+ <span className='dynamic-value__empty'>There is no value</span>
1536
+ )}
1537
+ </>
1538
+ ) : (
1539
+ ''
1540
+ )
1541
+ }
1542
+ placement={props.popupPlacement || 'bottom'}
1543
+ leaveDelay={400}
1544
+ >
1545
+ <span
1546
+ className={[
1547
+ 'dynamic-value',
1548
+ `dynamic-value__background--${valueMetadata.color}`,
1549
+ // canSwitch ? 'dynamic-value__no-end-cap' : '',
1550
+ !!relevantParentDisabledReasons.length ? 'disabled' : '',
1551
+ ].join(' ')}
1552
+ id={id}
1553
+ ref={canvasPopup?.anchorRef}
1554
+ onDoubleClick={(e) => {
1555
+ e.stopPropagation();
1556
+ e.nativeEvent.stopImmediatePropagation();
1557
+ }}
1558
+ >
1559
+ <div
1560
+ className={[
1561
+ 'dynamic-value__element',
1562
+ 'dynamic-value__type',
1563
+ 'dynamic-value__missing',
1564
+ ].join(' ')}
1565
+ >
1566
+ {dynamicValue?.type === DynamicValueTypes.Testing && 'Test'}
1567
+ {dynamicValue?.type === DynamicValueTypes.ExecutionResult && (
1568
+ <i className='fa-solid fa-check' />
1569
+ )}
1570
+ {dynamicValue?.type === DynamicValueTypes.Missing && (
1571
+ <img
1572
+ src={WarningIcon}
1573
+ style={{ width: '18px', height: '18px' }}
1574
+ id='play-test-button-warning-icon'
1575
+ />
1576
+ )}
1577
+ </div>
1578
+ {dynamicValue?.type === DynamicValueTypes.Missing && (
1579
+ <button
1580
+ className={[
1581
+ 'dynamic-value__element',
1582
+ 'dynamic-value__missing',
1583
+ 'dynamic-value__value',
1584
+ canEdit ? 'dynamic-value__value--editable' : '',
1585
+ ].join(' ')}
1586
+ onDoubleClick={(e: React.MouseEvent<HTMLButtonElement>) => {
1587
+ e.stopPropagation();
1588
+
1589
+ // This stops the native DOM bubbling that the Canvas Engine is listening to
1590
+ e.nativeEvent.stopImmediatePropagation();
1591
+ e.nativeEvent.stopPropagation();
1592
+
1593
+ if (canEdit) {
1594
+ let nextValueType = DynamicValueTypes.DefaultValue;
1595
+ handleSetPreference(nextValueType);
1596
+
1597
+ props.onEdit?.(e, nextValueType);
1598
+ }
1599
+ }}
1600
+ onMouseDown={(e: any) => {
1601
+ e.stopPropagation();
1602
+
1603
+ // This stops the native DOM bubbling that the Canvas Engine is listening to
1604
+ e.nativeEvent.stopImmediatePropagation();
1605
+ e.nativeEvent.stopPropagation();
1606
+ }}
1607
+ >
1608
+ Missing
1609
+ </button>
1610
+ )}
1611
+ {dynamicValue?.type === DynamicValueTypes.Inherited && (
1612
+ <button
1613
+ className={[
1614
+ 'dynamic-value__element',
1615
+ 'dynamic-value__missing',
1616
+ 'dynamic-value__value',
1617
+ canEdit ? 'dynamic-value__value--editable' : '',
1618
+ ].join(' ')}
1619
+ onDoubleClick={(e: React.MouseEvent<HTMLButtonElement>) => {
1620
+ e.stopPropagation();
1621
+
1622
+ // This stops the native DOM bubbling that the Canvas Engine is listening to
1623
+ e.nativeEvent.stopImmediatePropagation();
1624
+ e.nativeEvent.stopPropagation();
1625
+
1626
+ if (canEdit) {
1627
+ props.onEdit(e, finalResolvedPreference);
1628
+ }
1629
+ }}
1630
+ onMouseDown={(e: any) => {
1631
+ e.stopPropagation();
1632
+
1633
+ // This stops the native DOM bubbling that the Canvas Engine is listening to
1634
+ e.nativeEvent.stopImmediatePropagation();
1635
+ e.nativeEvent.stopPropagation();
1636
+ }}
1637
+ >
1638
+ Inherited
1639
+ </button>
1640
+ )}
1641
+ {dynamicValue?.type === DynamicValueTypes.External &&
1642
+ checkIsLiteralValueEmpty(dynamicValue?.value) && (
1643
+ <button
1644
+ className={[
1645
+ 'dynamic-value__element',
1646
+ 'dynamic-value__missing',
1647
+ 'dynamic-value__value',
1648
+ canEdit ? 'dynamic-value__value--editable' : '',
1649
+ ].join(' ')}
1650
+ onDoubleClick={(e: React.MouseEvent<HTMLButtonElement>) => {
1651
+ e.stopPropagation();
1652
+
1653
+ // This stops the native DOM bubbling that the Canvas Engine is listening to
1654
+ e.nativeEvent.stopImmediatePropagation();
1655
+ e.nativeEvent.stopPropagation();
1656
+
1657
+ if (canEdit) {
1658
+ props.onEdit(e, finalResolvedPreference);
1659
+ }
1660
+ }}
1661
+ onMouseDown={(e: any) => {
1662
+ e.stopPropagation();
1663
+
1664
+ // This stops the native DOM bubbling that the Canvas Engine is listening to
1665
+ e.nativeEvent.stopImmediatePropagation();
1666
+ e.nativeEvent.stopPropagation();
1667
+ }}
1668
+ >
1669
+ External
1670
+ </button>
1671
+ )}
1672
+ {dynamicValue?.type === DynamicValueTypes.QueuedAutocalculation && (
1673
+ <button
1674
+ className={[
1675
+ 'dynamic-value__element',
1676
+ 'dynamic-value__missing',
1677
+ 'dynamic-value__value',
1678
+ canEdit ? 'dynamic-value__value--editable' : '',
1679
+ ].join(' ')}
1680
+ onDoubleClick={(e: React.MouseEvent<HTMLButtonElement>) => {
1681
+ e.stopPropagation();
1682
+
1683
+ // This stops the native DOM bubbling that the Canvas Engine is listening to
1684
+ e.nativeEvent.stopImmediatePropagation();
1685
+ e.nativeEvent.stopPropagation();
1686
+
1687
+ if (canEdit) {
1688
+ props.onEdit(e, finalResolvedPreference);
1689
+ }
1690
+ }}
1691
+ onMouseDown={(e: any) => {
1692
+ e.stopPropagation();
1693
+
1694
+ // This stops the native DOM bubbling that the Canvas Engine is listening to
1695
+ e.nativeEvent.stopImmediatePropagation();
1696
+ e.nativeEvent.stopPropagation();
1697
+ }}
1698
+ >
1699
+ Calculated
1700
+ </button>
1701
+ )}
1702
+ {dynamicValue?.type === DynamicValueTypes.Undefined && (
1703
+ <button
1704
+ className={[
1705
+ 'dynamic-value__element',
1706
+ 'dynamic-value__empty',
1707
+ 'dynamic-value__value',
1708
+ canEdit ? 'dynamic-value__value--editable' : '',
1709
+ 'dynamic-value__undefined',
1710
+ ].join(' ')}
1711
+ onDoubleClick={(e: React.MouseEvent<HTMLButtonElement>) => {
1712
+ e.stopPropagation();
1713
+
1714
+ // This stops the native DOM bubbling that the Canvas Engine is listening to
1715
+ e.nativeEvent.stopImmediatePropagation();
1716
+ e.nativeEvent.stopPropagation();
1717
+
1718
+ if (canEdit) {
1719
+ props.onEdit(e, finalResolvedPreference);
1720
+ }
1721
+ }}
1722
+ onMouseDown={(e: any) => {
1723
+ e.stopPropagation();
1724
+
1725
+ // This stops the native DOM bubbling that the Canvas Engine is listening to
1726
+ e.nativeEvent.stopImmediatePropagation();
1727
+ e.nativeEvent.stopPropagation();
1728
+ }}
1729
+ >
1730
+ Undefined
1731
+ </button>
1732
+ )}
1733
+ {dynamicValue?.type === DynamicValueTypes.Empty && (
1734
+ <button
1735
+ className={[
1736
+ 'dynamic-value__element',
1737
+ 'dynamic-value__empty',
1738
+ 'dynamic-value__value',
1739
+ canEdit ? 'dynamic-value__value--editable' : '',
1740
+ ].join(' ')}
1741
+ onDoubleClick={(e: React.MouseEvent<HTMLButtonElement>) => {
1742
+ e.stopPropagation();
1743
+
1744
+ // This stops the native DOM bubbling that the Canvas Engine is listening to
1745
+ e.nativeEvent.stopImmediatePropagation();
1746
+ e.nativeEvent.stopPropagation();
1747
+
1748
+ if (canEdit) {
1749
+ props.onEdit(e, finalResolvedPreference);
1750
+ }
1751
+ }}
1752
+ onMouseDown={(e: any) => {
1753
+ e.stopPropagation();
1754
+
1755
+ // This stops the native DOM bubbling that the Canvas Engine is listening to
1756
+ e.nativeEvent.stopImmediatePropagation();
1757
+ e.nativeEvent.stopPropagation();
1758
+ }}
1759
+ >
1760
+ Empty
1761
+ </button>
1762
+ )}
1763
+ {[
1764
+ DynamicValueTypes.AutoCalculated,
1765
+ DynamicValueTypes.ExecutionResult,
1766
+ DynamicValueTypes.Testing,
1767
+ ].includes(dynamicValue?.type) && (
1768
+ <button
1769
+ className={[
1770
+ 'dynamic-value__element',
1771
+ !checkIsLiteralValueEmpty(dynamicValue?.value)
1772
+ ? ''
1773
+ : 'dynamic-value__empty',
1774
+ // 'dynamic-value__element--separator',
1775
+ canEdit ? 'dynamic-value__value--editable' : '',
1776
+ canEdit &&
1777
+ !props.parentDisabledReasons.length &&
1778
+ !checkIsLiteralValueEmpty(dynamicValue?.value)
1779
+ ? 'dynamic-value__value--inspectable'
1780
+ : '',
1781
+ ].join(' ')}
1782
+ onClick={(e: any) => {
1783
+ if (
1784
+ !canEdit &&
1785
+ !props.parentDisabledReasons.length &&
1786
+ !checkIsLiteralValueEmpty(dynamicValue?.value)
1787
+ ) {
1788
+ props.project.canvas?.unselectAll();
1789
+ props.project.emit('complex-data-dialog-open', {
1790
+ value: dynamicValue?.value,
1791
+ valueOwner: valueOwner,
1792
+ valueType: dynamicValue?.type,
1793
+ draggableObject: props.parentCanvasObject,
1794
+ project: props.project,
1795
+ });
1796
+ }
1797
+ }}
1798
+ onDoubleClick={(e: React.MouseEvent<HTMLButtonElement>) => {
1799
+ e.stopPropagation();
1800
+
1801
+ // This stops the native DOM bubbling that the Canvas Engine is listening to
1802
+ e.nativeEvent.stopImmediatePropagation();
1803
+ e.nativeEvent.stopPropagation();
1804
+
1805
+ if (canEdit && !props.parentDisabledReasons.length) {
1806
+ props.onEdit(e, finalResolvedPreference);
1807
+ } else if (
1808
+ !canEdit &&
1809
+ !props.parentDisabledReasons.length &&
1810
+ !checkIsLiteralValueEmpty(dynamicValue?.value)
1811
+ ) {
1812
+ props.project.canvas?.unselectAll();
1813
+ props.project.emit('complex-data-dialog-open', {
1814
+ value: dynamicValue?.value,
1815
+ valueOwner: valueOwner,
1816
+ valueType: dynamicValue?.type,
1817
+ draggableObject: props.parentCanvasObject,
1818
+ project: props.project,
1819
+ });
1820
+ }
1821
+ }}
1822
+ >
1823
+ {resolveLabelForValue(dynamicValue?.value, valueOwner)}
1824
+ </button>
1825
+ )}
1826
+ {![
1827
+ DynamicValueTypes.AutoCalculated,
1828
+ DynamicValueTypes.ExecutionResult,
1829
+ DynamicValueTypes.Testing,
1830
+ DynamicValueTypes.Inherited,
1831
+ DynamicValueTypes.Missing,
1832
+ DynamicValueTypes.Empty,
1833
+ DynamicValueTypes.External,
1834
+ DynamicValueTypes.QueuedAutocalculation,
1835
+ DynamicValueTypes.Undefined,
1836
+ ].includes(dynamicValue?.type) && (
1837
+ <button
1838
+ className={[
1839
+ 'dynamic-value__element',
1840
+ !checkIsLiteralValueEmpty(dynamicValue?.value)
1841
+ ? ''
1842
+ : 'dynamic-value__empty',
1843
+ 'dynamic-value__value',
1844
+ // 'dynamic-value__element--separator',
1845
+ canEdit ? 'dynamic-value__value--editable' : '',
1846
+ canEdit &&
1847
+ !props.parentDisabledReasons.length &&
1848
+ !checkIsLiteralValueEmpty(dynamicValue?.value)
1849
+ ? 'dynamic-value__value--inspectable'
1850
+ : '',
1851
+ ].join(' ')}
1852
+ onClick={(e: any) => {
1853
+ if (
1854
+ !canEdit &&
1855
+ !props.parentDisabledReasons.length &&
1856
+ !checkIsLiteralValueEmpty(dynamicValue?.value)
1857
+ ) {
1858
+ props.project.canvas?.unselectAll();
1859
+ props.project.emit('complex-data-dialog-open', {
1860
+ value: dynamicValue?.value,
1861
+ valueOwner: valueOwner,
1862
+ valueType: dynamicValue?.type,
1863
+ draggableObject: props.parentCanvasObject,
1864
+ project: props.project,
1865
+ });
1866
+ }
1867
+ }}
1868
+ onDoubleClick={(e: React.MouseEvent<HTMLButtonElement>) => {
1869
+ e.stopPropagation();
1870
+
1871
+ // This stops the native DOM bubbling that the Canvas Engine is listening to
1872
+ e.nativeEvent.stopImmediatePropagation();
1873
+ e.nativeEvent.stopPropagation();
1874
+
1875
+ if (canEdit && !props.parentDisabledReasons.length) {
1876
+ props.onEdit(e, finalResolvedPreference);
1877
+ } else if (
1878
+ !canEdit &&
1879
+ !props.parentDisabledReasons.length &&
1880
+ !checkIsLiteralValueEmpty(dynamicValue?.value)
1881
+ ) {
1882
+ props.project.canvas?.unselectAll();
1883
+ props.project.emit('complex-data-dialog-open', {
1884
+ value: dynamicValue?.value,
1885
+ valueOwner: valueOwner,
1886
+ valueType: dynamicValue?.type,
1887
+ draggableObject: props.parentCanvasObject,
1888
+ project: props.project,
1889
+ });
1890
+ }
1891
+ }}
1892
+ >
1893
+ {resolveLabelForValue(dynamicValue?.value, valueOwner)}
1894
+ </button>
1895
+ )}
1896
+ {/* {canCopyValue && (
1897
+ <button
1898
+ className={[
1899
+ 'dynamic-value__element',
1900
+ 'dynamic-value__option',
1901
+ 'dynamic-value__option--copy',
1902
+ 'dynamic-value__element--clickable',
1903
+ ].join(' ')}
1904
+ onClick={debouncedCopyAndNotify}
1905
+ >
1906
+ <i className='fa-regular fa-copy' />
1907
+ </button>
1908
+ )} */}
1909
+ {!relevantParentDisabledReasons.length && (
1910
+ <>
1911
+ <button
1912
+ className={[
1913
+ 'dynamic-value__element',
1914
+ 'dynamic-value__option',
1915
+ 'dynamic-value__option--copy',
1916
+ 'dynamic-value__element--clickable',
1917
+ ].join(' ')}
1918
+ id={`${id}--dropdown-menu-button`}
1919
+ ref={canvasMenuDropdown?.anchorRef}
1920
+ onClick={
1921
+ !!canvasMenuDropdown
1922
+ ? () => {
1923
+ canvasMenuDropdown?.toggle();
1924
+ }
1925
+ : () => {
1926
+ setDropdownOpen(!dropdownOpen);
1927
+ }
1928
+ }
1929
+ >
1930
+ <i className='fa-regular fa-chevron-down' />
1931
+ </button>
1932
+
1933
+ {!canvasMenuDropdown && (
1934
+ <DropdownMenuPopup
1935
+ // id={`dynamic-value-dropdown-menu--${id}`}
1936
+ popupOptions={{
1937
+ anchorQuerySelector: `#${id}--dropdown-menu-button`,
1938
+ placement: 'bottom',
1939
+ open: dropdownOpen,
1940
+ onClickAway() {
1941
+ setDropdownOpen(false);
1942
+ },
1943
+ clickAwayTargets: (() => {
1944
+ const dialog = document.getElementById('dialog-card');
1945
+
1946
+ if (dialog) {
1947
+ return [document.body, dialog];
1948
+ }
1949
+
1950
+ return [document.body];
1951
+ })(),
1952
+ }}
1953
+ options={[
1954
+ {
1955
+ onClick: async (_, e) => {
1956
+ if (canEdit) {
1957
+ let nextValueType = DynamicValueTypes.DefaultValue;
1958
+ handleSetPreference(nextValueType);
1959
+
1960
+ props.onEdit?.(e, nextValueType);
1961
+ }
1962
+ },
1963
+ label: 'Edit',
1964
+ iconEnd: 'fa-solid fa-pencil',
1965
+ disabled: !canEdit,
1966
+ tooltip: !canEdit
1967
+ ? 'Cannot edit this value'
1968
+ : 'Edit the value (you can also double click the value directly)',
1969
+ id: 'canvas-dropdown-menu--' + id + '--edit',
1970
+ },
1971
+ ...(() => {
1972
+ if (!!valueOwnerDataType && valueOwnerDataType.asType) {
1973
+ const asTypeValue = dynamicValue?.value?.valueAsType;
1974
+ const thereAreMultipleEntityReferences =
1975
+ Array.isArray(asTypeValue) && asTypeValue.length > 1;
1976
+ const isEmpty =
1977
+ !asTypeValue ||
1978
+ (Array.isArray(asTypeValue) &&
1979
+ asTypeValue.length === 0);
1980
+
1981
+ let target: ValueAsTypeState | null =
1982
+ asTypeValue as ValueAsTypeState;
1983
+
1984
+ if (thereAreMultipleEntityReferences) {
1985
+ target = (asTypeValue as ValueAsTypeState[])[0];
1986
+ }
1987
+
1988
+ let definitionName = '';
1989
+
1990
+ let canvasEntity: CanvasEntityState | null = null;
1991
+
1992
+ if (target) {
1993
+ definitionName = resolveEntityName(
1994
+ target,
1995
+ props.project.logic,
1996
+ );
1997
+
1998
+ canvasEntity = getParentCanvasEntity(target);
1999
+ }
2000
+
2001
+ return [
2002
+ {
2003
+ onClick: async (
2004
+ _: any,
2005
+ e: React.MouseEvent<
2006
+ HTMLButtonElement | HTMLAnchorElement,
2007
+ MouseEvent
2008
+ >,
2009
+ ) => {
2010
+ e.stopPropagation();
2011
+
2012
+ // This stops the native DOM bubbling that the Canvas Engine is listening to
2013
+ e.nativeEvent.stopImmediatePropagation();
2014
+ e.nativeEvent.stopPropagation();
2015
+
2016
+ if (isEmpty) {
2017
+ return;
2018
+ }
2019
+
2020
+ if (!canvasEntity) {
2021
+ return;
2022
+ }
2023
+
2024
+ const objectId =
2025
+ getCanvasEntityDraggableContainerDOMId(
2026
+ canvasEntity,
2027
+ );
2028
+
2029
+ props.project?.canvas?.scrollToObjectById(objectId);
2030
+ },
2031
+ label: thereAreMultipleEntityReferences
2032
+ ? 'Go to first definition'
2033
+ : 'Go to definition',
2034
+ iconEnd: 'fa-solid fa-arrow-up-right-from-square',
2035
+ disabled: isEmpty || !canvasEntity,
2036
+ tooltip: isEmpty
2037
+ ? 'No definition has been set'
2038
+ : !canvasEntity
2039
+ ? 'Definition is not in the canvas'
2040
+ : thereAreMultipleEntityReferences
2041
+ ? `Go to the first definition of this value (${definitionName})`
2042
+ : `Go to ${definitionName} in the canvas`,
2043
+ id:
2044
+ 'canvas-dropdown-menu--' +
2045
+ id +
2046
+ '--go-to-definition',
2047
+ },
2048
+ ];
2049
+ }
2050
+
2051
+ return [];
2052
+ })(),
2053
+ {
2054
+ onClick: async (_, e) => {
2055
+ if (canDelete) {
2056
+ props.onDelete?.(dynamicValue, e);
2057
+ }
2058
+ },
2059
+ label: 'Delete',
2060
+ color: 'error',
2061
+ iconEnd: 'fa-solid fa-trash-can',
2062
+ disabled: !canDelete,
2063
+ tooltip: !canDelete
2064
+ ? 'Cannot delete this value'
2065
+ : 'Remove the value to not override any previous value that might be present',
2066
+ id: 'canvas-dropdown-menu--' + id + '--delete',
2067
+ },
2068
+ {
2069
+ onClick: debouncedCopyAndNotify,
2070
+ label: 'Copy value',
2071
+ color: 'primary',
2072
+ iconEnd: 'fa-regular fa-copy',
2073
+ disabled: !canCopyValue,
2074
+ tooltip: !canCopyValue ? 'Cannot copy this value' : '',
2075
+ id: 'canvas-dropdown-menu--' + id + '--copy',
2076
+ },
2077
+ {
2078
+ onClick: async () => {
2079
+ if (!canSetToNull) {
2080
+ return;
2081
+ }
2082
+
2083
+ const localOnlyDefault =
2084
+ finalResolvedPreference ===
2085
+ DynamicValueTypes.DefaultValue || onlyDefault;
2086
+
2087
+ _handleLiteralValueChange(
2088
+ null,
2089
+ localOnlyDefault,
2090
+ StateMutationAction.SetLiteralValueToExplicitlyEmpty,
2091
+ );
2092
+ },
2093
+ label: 'Set as empty',
2094
+ color: 'primary',
2095
+ iconEnd: 'fa-solid fa-empty-set',
2096
+ disabled: !canSetToNull,
2097
+ tooltip: !canSetToNull
2098
+ ? 'Cannot set this value as empty' +
2099
+ (!checkIsNullable(valueOwnerDataType)
2100
+ ? '. The data type doesn not support empty values'
2101
+ : '')
2102
+ : 'Sets a value explicitly as empty (null), to override any previous value that might be present',
2103
+ id: 'canvas-dropdown-menu--' + id + '--set-empty',
2104
+ },
2105
+ {
2106
+ onClick: async (_, e) => {
2107
+ if (canSwitch) {
2108
+ let nextValueType = finalResolvedPreference as
2109
+ | DynamicValueTypes.DefaultValue
2110
+ | DynamicValueTypes.Testing;
2111
+
2112
+ // if (
2113
+ // dynamicValue?.type === DynamicValueTypes.Inherited &&
2114
+ // valueOwner.type === EntityType.InputMap
2115
+ // ) {
2116
+ // finalResolvedPreference === DynamicValueTypes.DefaultValue;
2117
+ // } else if (
2118
+ if (
2119
+ finalResolvedPreference ===
2120
+ DynamicValueTypes.DefaultValue
2121
+ ) {
2122
+ nextValueType = DynamicValueTypes.Testing;
2123
+ } else if (
2124
+ finalResolvedPreference === DynamicValueTypes.Testing
2125
+ ) {
2126
+ nextValueType = DynamicValueTypes.DefaultValue;
2127
+ } else if (
2128
+ dynamicValue?.type === DynamicValueTypes.Testing
2129
+ ) {
2130
+ nextValueType = DynamicValueTypes.DefaultValue;
2131
+ } else {
2132
+ // If current value is neither default nor testing, switch to the other type
2133
+ nextValueType = DynamicValueTypes.Testing;
2134
+ }
2135
+
2136
+ handleSetPreference(nextValueType);
2137
+
2138
+ // if (
2139
+ // dynamicValue?.type === DynamicValueTypes.Inherited &&
2140
+ // valueOwner.type === EntityType.InputMap
2141
+ // ) {
2142
+ // props.onEdit?.(e, nextValueType);
2143
+ // } else if (!counterpartTypeExists) {
2144
+ if (!counterpartTypeExists) {
2145
+ props.onEdit?.(e, nextValueType);
2146
+ }
2147
+ }
2148
+ },
2149
+ label: (() => {
2150
+ if (
2151
+ dynamicValue.type === DynamicValueTypes.Testing &&
2152
+ canSwitch
2153
+ ) {
2154
+ if (!counterpartTypeExists) {
2155
+ return 'Click to add real value';
2156
+ } else {
2157
+ return 'Use real value';
2158
+ }
2159
+ }
2160
+
2161
+ if (
2162
+ dynamicValue.type !== DynamicValueTypes.Testing &&
2163
+ canSwitch
2164
+ ) {
2165
+ if (!counterpartTypeExists) {
2166
+ return 'Click to add test value';
2167
+ } else {
2168
+ return 'Use test value';
2169
+ }
2170
+ }
2171
+
2172
+ return 'Switch values';
2173
+ })(),
2174
+ color: 'primary',
2175
+ iconEnd:
2176
+ 'fa-solid fa-' +
2177
+ (counterpartTypeExists ? 'retweet' : 'plus'),
2178
+ disabled: (() => {
2179
+ if (
2180
+ dynamicValue.type === DynamicValueTypes.Testing &&
2181
+ canSwitch
2182
+ ) {
2183
+ return false;
2184
+ }
2185
+
2186
+ if (
2187
+ dynamicValue.type !== DynamicValueTypes.Testing &&
2188
+ canSwitch
2189
+ ) {
2190
+ return false;
2191
+ }
2192
+
2193
+ return true;
2194
+ })(),
2195
+ tooltip: (() => {
2196
+ if (
2197
+ dynamicValue.type === DynamicValueTypes.Testing &&
2198
+ canSwitch
2199
+ ) {
2200
+ return 'Switch the active value to use the real value that will be published with the project';
2201
+ }
2202
+
2203
+ if (
2204
+ dynamicValue.type !== DynamicValueTypes.Testing &&
2205
+ canSwitch
2206
+ ) {
2207
+ return 'Switch the active value to use a temporary value for testing purposes';
2208
+ }
2209
+
2210
+ // Disabled explanations
2211
+
2212
+ return 'Switch active values';
2213
+ })(),
2214
+ id: 'canvas-dropdown-menu--' + id + '--switch-values',
2215
+ },
2216
+ ]}
2217
+ />
2218
+ )}
2219
+ </>
2220
+ )}
2221
+
2222
+ {/* {canDelete && (
2223
+ <button
2224
+ className={[
2225
+ 'dynamic-value__element',
2226
+ 'dynamic-value__option',
2227
+ 'dynamic-value__element--clickable',
2228
+ ].join(' ')}
2229
+ onClick={(e: any) => {
2230
+ if (canDelete) {
2231
+ props.onDelete?.(dynamicValue, e);
2232
+ }
2233
+ }}
2234
+ >
2235
+ <i className='fa fa-times' />
2236
+ </button>
2237
+ )} */}
2238
+ {false && (
2239
+ <Tooltip
2240
+ // The styles overrides below can be cleaned up
2241
+ classes={{
2242
+ tooltip: 'tooltip tooltip--dynamic-value',
2243
+ arrow: 'mui-tooltip-arrow',
2244
+ }}
2245
+ slotProps={{
2246
+ popper: {
2247
+ className: 'tooltip tooltip--dynamic-value',
2248
+ style: {
2249
+ margin: 0,
2250
+ padding: 0,
2251
+ },
2252
+ modifiers: [
2253
+ {
2254
+ name: 'offset',
2255
+ options: {
2256
+ offset: [0, 8],
2257
+ },
2258
+ },
2259
+ ],
2260
+ },
2261
+ tooltip: {
2262
+ className: 'tooltip tooltip--dynamic-value',
2263
+ style: {
2264
+ margin: 0,
2265
+ padding: 5,
2266
+ },
2267
+ },
2268
+ arrow: {
2269
+ className: 'mui-tooltip-arrow',
2270
+ },
2271
+ }}
2272
+ arrow
2273
+ title={
2274
+ !props.parentCanvasObject &&
2275
+ !props.parentDisabledReasons.length ? (
2276
+ <>
2277
+ {!counterpartTypeExists ? (
2278
+ <>
2279
+ Click to add new{' '}
2280
+ <b>
2281
+ {dynamicValue?.type === DynamicValueTypes.DefaultValue
2282
+ ? 'test'
2283
+ : 'default'}{' '}
2284
+ value
2285
+ </b>
2286
+ </>
2287
+ ) : (
2288
+ <>
2289
+ Click to switch to{' '}
2290
+ <b>
2291
+ {dynamicValue?.type === DynamicValueTypes.DefaultValue
2292
+ ? 'test'
2293
+ : 'default'}{' '}
2294
+ value
2295
+ </b>
2296
+ </>
2297
+ )}
2298
+ </>
2299
+ ) : (
2300
+ ''
2301
+ )
2302
+ }
2303
+ placement={props.popupPlacement || 'bottom'}
2304
+ leaveDelay={400}
2305
+ >
2306
+ <button
2307
+ id={`switch-dynamic-value-type-button--${id}`}
2308
+ ref={canvasPopupSwitchButton?.anchorRef}
2309
+ className={[
2310
+ 'dynamic-value__element',
2311
+ 'dynamic-value__option',
2312
+ 'dynamic-value__element--clickable',
2313
+ 'dynamic-value__element--end-cap',
2314
+ // ...(() => {
2315
+ // if (dynamicValue?.type === DynamicValueTypes.Missing) {
2316
+ // return [
2317
+ // 'dynamic-value__element--separator--error',
2318
+ // 'dynamic-value__option--switch-to-missing-default',
2319
+ // ];
2320
+ // }
2321
+
2322
+ // if (
2323
+ // dynamicValue?.type === DynamicValueTypes.DefaultValue ||
2324
+ // dynamicValue?.type === DynamicValueTypes.External ||
2325
+ // dynamicValue?.type === DynamicValueTypes.Empty
2326
+ // ) {
2327
+ // return [
2328
+ // 'dynamic-value__element--separator--primary',
2329
+ // 'dynamic-value__option--switch-to-test',
2330
+ // ];
2331
+ // }
2332
+
2333
+ // if (dynamicValue?.type === DynamicValueTypes.Testing) {
2334
+ // return [
2335
+ // 'dynamic-value__element--separator--grey',
2336
+ // 'dynamic-value__option--switch-to-default',
2337
+ // ];
2338
+ // }
2339
+
2340
+ // return [];
2341
+ // })(),
2342
+ ].join(' ')}
2343
+ onClick={(e: any) => {
2344
+ if (canSwitch) {
2345
+ let nextValueType = finalResolvedPreference as
2346
+ | DynamicValueTypes.DefaultValue
2347
+ | DynamicValueTypes.Testing;
2348
+
2349
+ // if (
2350
+ // dynamicValue?.type === DynamicValueTypes.Inherited &&
2351
+ // valueOwner.type === EntityType.InputMap
2352
+ // ) {
2353
+ // finalResolvedPreference === DynamicValueTypes.DefaultValue;
2354
+ // } else if (
2355
+ if (
2356
+ finalResolvedPreference === DynamicValueTypes.DefaultValue
2357
+ ) {
2358
+ nextValueType = DynamicValueTypes.Testing;
2359
+ } else if (
2360
+ finalResolvedPreference === DynamicValueTypes.Testing
2361
+ ) {
2362
+ nextValueType = DynamicValueTypes.DefaultValue;
2363
+ } else if (dynamicValue?.type === DynamicValueTypes.Testing) {
2364
+ nextValueType = DynamicValueTypes.DefaultValue;
2365
+ } else {
2366
+ // If current value is neither default nor testing, switch to the other type
2367
+ nextValueType = DynamicValueTypes.Testing;
2368
+ }
2369
+
2370
+ handleSetPreference(nextValueType);
2371
+
2372
+ // if (
2373
+ // dynamicValue?.type === DynamicValueTypes.Inherited &&
2374
+ // valueOwner.type === EntityType.InputMap
2375
+ // ) {
2376
+ // props.onEdit?.(e, nextValueType);
2377
+ // } else if (!counterpartTypeExists) {
2378
+ if (!counterpartTypeExists) {
2379
+ props.onEdit?.(e, nextValueType);
2380
+ }
2381
+ }
2382
+ }}
2383
+ >
2384
+ <i
2385
+ className={`fa fa-${
2386
+ counterpartTypeExists ? 'retweet' : 'plus'
2387
+ }`}
2388
+ />
2389
+ </button>
2390
+ </Tooltip>
2391
+ )}
2392
+ </span>
2393
+ </Tooltip>
2394
+ );
2395
+ }