@nyaruka/temba-components 0.129.3 → 0.129.4
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.
- package/.eslintrc.js +1 -0
- package/.github/workflows/build.yml +135 -3
- package/CHANGELOG.md +18 -0
- package/demo/data/flows/sample-flow.json +110 -87
- package/demo/field-config-demo.html +135 -0
- package/dist/temba-components.js +1257 -675
- package/dist/temba-components.js.map +1 -1
- package/docs/ActionEditor-Migration.md +118 -0
- package/out-tsc/src/events.js.map +1 -1
- package/out-tsc/src/flow/{EditorNode.js → CanvasNode.js} +345 -42
- package/out-tsc/src/flow/CanvasNode.js.map +1 -0
- package/out-tsc/src/flow/Editor.js +107 -3
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +1200 -0
- package/out-tsc/src/flow/NodeEditor.js.map +1 -0
- package/out-tsc/src/flow/Plumber.js +0 -6
- package/out-tsc/src/flow/Plumber.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_groups.js +40 -0
- package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -0
- package/out-tsc/src/flow/actions/add_contact_urn.js +16 -0
- package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -0
- package/out-tsc/src/flow/actions/add_input_labels.js +11 -0
- package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -0
- package/out-tsc/src/flow/actions/call_classifier.js +11 -0
- package/out-tsc/src/flow/actions/call_classifier.js.map +1 -0
- package/out-tsc/src/flow/actions/call_llm.js +11 -0
- package/out-tsc/src/flow/actions/call_llm.js.map +1 -0
- package/out-tsc/src/flow/actions/call_resthook.js +11 -0
- package/out-tsc/src/flow/actions/call_resthook.js.map +1 -0
- package/out-tsc/src/flow/actions/call_webhook.js +122 -0
- package/out-tsc/src/flow/actions/call_webhook.js.map +1 -0
- package/out-tsc/src/flow/actions/enter_flow.js +14 -0
- package/out-tsc/src/flow/actions/enter_flow.js.map +1 -0
- package/out-tsc/src/flow/actions/open_ticket.js +11 -0
- package/out-tsc/src/flow/actions/open_ticket.js.map +1 -0
- package/out-tsc/src/flow/actions/play_audio.js +11 -0
- package/out-tsc/src/flow/actions/play_audio.js.map +1 -0
- package/out-tsc/src/flow/actions/remove_contact_groups.js +62 -0
- package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -0
- package/out-tsc/src/flow/actions/request_optin.js +11 -0
- package/out-tsc/src/flow/actions/request_optin.js.map +1 -0
- package/out-tsc/src/flow/actions/say_msg.js +11 -0
- package/out-tsc/src/flow/actions/say_msg.js.map +1 -0
- package/out-tsc/src/flow/actions/send_broadcast.js +33 -0
- package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -0
- package/out-tsc/src/flow/actions/send_email.js +56 -0
- package/out-tsc/src/flow/actions/send_email.js.map +1 -0
- package/out-tsc/src/flow/actions/send_msg.js +55 -0
- package/out-tsc/src/flow/actions/send_msg.js.map +1 -0
- package/out-tsc/src/flow/actions/set_contact_channel.js +12 -0
- package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -0
- package/out-tsc/src/flow/actions/set_contact_field.js +12 -0
- package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -0
- package/out-tsc/src/flow/actions/set_contact_language.js +10 -0
- package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -0
- package/out-tsc/src/flow/actions/set_contact_name.js +10 -0
- package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -0
- package/out-tsc/src/flow/actions/set_contact_status.js +10 -0
- package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -0
- package/out-tsc/src/flow/actions/set_run_result.js +10 -0
- package/out-tsc/src/flow/actions/set_run_result.js.map +1 -0
- package/out-tsc/src/flow/actions/split_by_expression_example.js +77 -0
- package/out-tsc/src/flow/actions/split_by_expression_example.js.map +1 -0
- package/out-tsc/src/flow/actions/start_session.js +11 -0
- package/out-tsc/src/flow/actions/start_session.js.map +1 -0
- package/out-tsc/src/flow/actions/transfer_airtime.js +11 -0
- package/out-tsc/src/flow/actions/transfer_airtime.js.map +1 -0
- package/out-tsc/src/flow/config.js +88 -193
- package/out-tsc/src/flow/config.js.map +1 -1
- package/out-tsc/src/flow/nodes/execute_actions.js +4 -0
- package/out-tsc/src/flow/nodes/execute_actions.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_airtime.js +9 -0
- package/out-tsc/src/flow/nodes/split_by_airtime.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_contact_field.js +7 -0
- package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_expression.js +7 -0
- package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_groups.js +7 -0
- package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_random.js +10 -0
- package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_run_result.js +7 -0
- package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_scheme.js +7 -0
- package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_subflow.js +9 -0
- package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_webhook.js +18 -0
- package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_audio.js +7 -0
- package/out-tsc/src/flow/nodes/wait_for_audio.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_digits.js +7 -0
- package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_image.js +7 -0
- package/out-tsc/src/flow/nodes/wait_for_image.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_location.js +7 -0
- package/out-tsc/src/flow/nodes/wait_for_location.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_menu.js +7 -0
- package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_response.js +7 -0
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_video.js +7 -0
- package/out-tsc/src/flow/nodes/wait_for_video.js.map +1 -0
- package/out-tsc/src/flow/types.js +79 -0
- package/out-tsc/src/flow/types.js.map +1 -0
- package/out-tsc/src/flow/utils.js +65 -0
- package/out-tsc/src/flow/utils.js.map +1 -0
- package/out-tsc/src/form/ArrayEditor.js +199 -0
- package/out-tsc/src/form/ArrayEditor.js.map +1 -0
- package/out-tsc/src/form/BaseListEditor.js +128 -0
- package/out-tsc/src/form/BaseListEditor.js.map +1 -0
- package/out-tsc/src/form/Checkbox.js +17 -2
- package/out-tsc/src/form/Checkbox.js.map +1 -1
- package/out-tsc/src/form/Completion.js +6 -0
- package/out-tsc/src/form/Completion.js.map +1 -1
- package/out-tsc/src/form/FormField.js +110 -11
- package/out-tsc/src/form/FormField.js.map +1 -1
- package/out-tsc/src/form/KeyValueEditor.js +223 -0
- package/out-tsc/src/form/KeyValueEditor.js.map +1 -0
- package/out-tsc/src/form/select/Select.js +77 -32
- package/out-tsc/src/form/select/Select.js.map +1 -1
- package/out-tsc/src/interfaces.js +6 -0
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +2 -76
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/temba-modules.js +9 -2
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/ActionHelper.js +116 -0
- package/out-tsc/test/ActionHelper.js.map +1 -0
- package/out-tsc/test/actions/add_contact_groups.test.js +66 -0
- package/out-tsc/test/actions/add_contact_groups.test.js.map +1 -0
- package/out-tsc/test/actions/remove_contact_groups.test.js +226 -0
- package/out-tsc/test/actions/remove_contact_groups.test.js.map +1 -0
- package/out-tsc/test/actions/send_email.test.js +160 -0
- package/out-tsc/test/actions/send_email.test.js.map +1 -0
- package/out-tsc/test/actions/send_msg.test.js +95 -0
- package/out-tsc/test/actions/send_msg.test.js.map +1 -0
- package/out-tsc/test/temba-action-editing-integration.test.js +183 -0
- package/out-tsc/test/temba-action-editing-integration.test.js.map +1 -0
- package/out-tsc/test/temba-checkbox.test.js +1 -1
- package/out-tsc/test/temba-checkbox.test.js.map +1 -1
- package/out-tsc/test/temba-field-config.test.js +133 -0
- package/out-tsc/test/temba-field-config.test.js.map +1 -0
- package/out-tsc/test/temba-flow-editor-node.test.js +14 -14
- package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
- package/out-tsc/test/temba-node-editor.test.js +283 -0
- package/out-tsc/test/temba-node-editor.test.js.map +1 -0
- package/out-tsc/test/temba-select.test.js +85 -0
- package/out-tsc/test/temba-select.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
- package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
- package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
- package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
- package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
- package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
- package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
- package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
- package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
- package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
- package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
- package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
- package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
- package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
- package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
- package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
- package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
- package/screenshots/truth/editor/router.png +0 -0
- package/screenshots/truth/editor/send_msg.png +0 -0
- package/screenshots/truth/editor/set_contact_language.png +0 -0
- package/screenshots/truth/editor/set_contact_name.png +0 -0
- package/screenshots/truth/editor/set_run_result.png +0 -0
- package/screenshots/truth/editor/wait.png +0 -0
- package/screenshots/truth/formfield/markdown-errors.png +0 -0
- package/screenshots/truth/formfield/plain-text-errors.png +0 -0
- package/screenshots/truth/formfield/widget-only-markdown-errors.png +0 -0
- package/screenshots/truth/integration/checkbox-markdown-errors.png +0 -0
- package/src/events.ts +1 -40
- package/src/flow/{EditorNode.ts → CanvasNode.ts} +424 -48
- package/src/flow/Editor.ts +140 -4
- package/src/flow/NodeEditor.ts +1443 -0
- package/src/flow/Plumber.ts +0 -9
- package/src/flow/actions/add_contact_groups.ts +42 -0
- package/src/flow/actions/add_contact_urn.ts +17 -0
- package/src/flow/actions/add_input_labels.ts +12 -0
- package/src/flow/actions/call_classifier.ts +12 -0
- package/src/flow/actions/call_llm.ts +12 -0
- package/src/flow/actions/call_resthook.ts +12 -0
- package/src/flow/actions/call_webhook.ts +133 -0
- package/src/flow/actions/enter_flow.ts +15 -0
- package/src/flow/actions/open_ticket.ts +12 -0
- package/src/flow/actions/play_audio.ts +12 -0
- package/src/flow/actions/remove_contact_groups.ts +66 -0
- package/src/flow/actions/request_optin.ts +12 -0
- package/src/flow/actions/say_msg.ts +12 -0
- package/src/flow/actions/send_broadcast.ts +35 -0
- package/src/flow/actions/send_email.ts +60 -0
- package/src/flow/actions/send_msg.ts +58 -0
- package/src/flow/actions/set_contact_channel.ts +13 -0
- package/src/flow/actions/set_contact_field.ts +13 -0
- package/src/flow/actions/set_contact_language.ts +11 -0
- package/src/flow/actions/set_contact_name.ts +11 -0
- package/src/flow/actions/set_contact_status.ts +11 -0
- package/src/flow/actions/set_run_result.ts +11 -0
- package/src/flow/actions/split_by_expression_example.ts +88 -0
- package/src/flow/actions/start_session.ts +12 -0
- package/src/flow/actions/transfer_airtime.ts +12 -0
- package/src/flow/config.ts +93 -232
- package/src/flow/nodes/execute_actions.ts +5 -0
- package/src/flow/nodes/split_by_airtime.ts +9 -0
- package/src/flow/nodes/split_by_contact_field.ts +7 -0
- package/src/flow/nodes/split_by_expression.ts +7 -0
- package/src/flow/nodes/split_by_groups.ts +7 -0
- package/src/flow/nodes/split_by_random.ts +10 -0
- package/src/flow/nodes/split_by_run_result.ts +7 -0
- package/src/flow/nodes/split_by_scheme.ts +7 -0
- package/src/flow/nodes/split_by_subflow.ts +9 -0
- package/src/flow/nodes/split_by_webhook.ts +19 -0
- package/src/flow/nodes/wait_for_audio.ts +7 -0
- package/src/flow/nodes/wait_for_digits.ts +7 -0
- package/src/flow/nodes/wait_for_image.ts +7 -0
- package/src/flow/nodes/wait_for_location.ts +7 -0
- package/src/flow/nodes/wait_for_menu.ts +7 -0
- package/src/flow/nodes/wait_for_response.ts +7 -0
- package/src/flow/nodes/wait_for_video.ts +7 -0
- package/src/flow/types.ts +352 -0
- package/src/flow/utils.ts +76 -0
- package/src/form/ArrayEditor.ts +240 -0
- package/src/form/BaseListEditor.ts +177 -0
- package/src/form/Checkbox.ts +22 -3
- package/src/form/Completion.ts +6 -0
- package/src/form/FormField.ts +115 -11
- package/src/form/KeyValueEditor.ts +251 -0
- package/src/form/select/Select.ts +89 -32
- package/src/interfaces.ts +7 -2
- package/src/live/ContactChat.ts +3 -97
- package/src/store/flow-definition.d.ts +6 -1
- package/static/api/contacts.json +30 -0
- package/static/api/groups.json +4 -426
- package/static/api/locations.json +24 -0
- package/static/api/media.json +5 -0
- package/static/api/optins.json +16 -0
- package/static/api/orgs.json +13 -0
- package/static/api/topics.json +21 -0
- package/static/api/users.json +26 -0
- package/static/css/temba-components.css +3 -6
- package/temba-modules.ts +9 -2
- package/test/ActionHelper.ts +142 -0
- package/test/actions/add_contact_groups.test.ts +89 -0
- package/test/actions/remove_contact_groups.test.ts +265 -0
- package/test/actions/send_email.test.ts +214 -0
- package/test/actions/send_msg.test.ts +130 -0
- package/test/temba-action-editing-integration.test.ts +240 -0
- package/test/temba-checkbox.test.ts +1 -1
- package/test/temba-field-config.test.ts +152 -0
- package/test/temba-flow-editor-node.test.ts +18 -18
- package/test/temba-node-editor.test.ts +353 -0
- package/test/temba-select.test.ts +127 -0
- package/test-assets/contacts/history.json +11 -33
- package/web-dev-server.config.mjs +34 -0
- package/.github/workflows/coverage.yml +0 -80
- package/demo/sticky-note-demo.html +0 -155
- package/out-tsc/src/flow/EditorNode.js.map +0 -1
- package/out-tsc/src/flow/render.js +0 -358
- package/out-tsc/src/flow/render.js.map +0 -1
- package/out-tsc/test/temba-flow-render.test.js +0 -794
- package/out-tsc/test/temba-flow-render.test.js.map +0 -1
- package/src/flow/render.ts +0 -443
- package/test/temba-flow-render.test.ts +0 -1003
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# ActionEditor Form Data Abstraction Migration Guide
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The ActionEditor now supports form-level data transformations that provide a higher level of abstraction than the previous per-property approach. This allows for flexible mapping between action properties and form fields.
|
|
6
|
+
|
|
7
|
+
## Key Changes
|
|
8
|
+
|
|
9
|
+
### 1. Action-Level Transformations
|
|
10
|
+
Instead of per-property `toFormValue`/`fromFormValue`, you can now define these at the action level:
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
export const myAction: UIConfig = {
|
|
14
|
+
// Transform entire action to form data
|
|
15
|
+
toFormValue: (action: MyAction) => ({
|
|
16
|
+
// Form data can combine multiple action properties
|
|
17
|
+
contact_info: `${action.name} <${action.email}>`,
|
|
18
|
+
selected_options: action.options.map(opt => ({ name: opt, value: opt }))
|
|
19
|
+
}),
|
|
20
|
+
|
|
21
|
+
// Transform form data back to action
|
|
22
|
+
fromFormValue: (formData: any): MyAction => {
|
|
23
|
+
// Parse the combined contact_info back to separate fields
|
|
24
|
+
const [name, email] = parseContactInfo(formData.contact_info);
|
|
25
|
+
return {
|
|
26
|
+
...action,
|
|
27
|
+
name,
|
|
28
|
+
email,
|
|
29
|
+
options: formData.selected_options.map(opt => opt.value)
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. Form Configuration (replaces properties)
|
|
36
|
+
Use `form` instead of `properties`, with keys matching your form data structure:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
export const myAction: UIConfig = {
|
|
40
|
+
// OLD: properties keys matched action properties
|
|
41
|
+
properties: {
|
|
42
|
+
name: { /* config */ },
|
|
43
|
+
email: { /* config */ }
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
// NEW: form keys match form data structure
|
|
47
|
+
form: {
|
|
48
|
+
contact_info: { /* combines name + email */ },
|
|
49
|
+
selected_options: { /* transforms options array */ }
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Migration Examples
|
|
55
|
+
|
|
56
|
+
### Before (per-property transformations)
|
|
57
|
+
```typescript
|
|
58
|
+
export const send_msg: UIConfig = {
|
|
59
|
+
properties: {
|
|
60
|
+
quick_replies: {
|
|
61
|
+
toFormValue: (actionValue: string[]) =>
|
|
62
|
+
actionValue.map(text => ({ name: text, value: text })),
|
|
63
|
+
fromFormValue: (formValue: Array<{name: string, value: string}>) =>
|
|
64
|
+
formValue.map(item => item.value)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### After (form-level transformations)
|
|
71
|
+
```typescript
|
|
72
|
+
export const send_msg_new: UIConfig = {
|
|
73
|
+
toFormValue: (action: SendMsg) => ({
|
|
74
|
+
text: action.text,
|
|
75
|
+
quick_replies: action.quick_replies.map(text => ({ name: text, value: text }))
|
|
76
|
+
}),
|
|
77
|
+
|
|
78
|
+
fromFormValue: (formData: any): SendMsg => ({
|
|
79
|
+
...formData,
|
|
80
|
+
type: 'send_msg',
|
|
81
|
+
quick_replies: formData.quick_replies.map(item => item.value || item.name)
|
|
82
|
+
}),
|
|
83
|
+
|
|
84
|
+
form: {
|
|
85
|
+
text: { /* config */ },
|
|
86
|
+
quick_replies: { /* config */ }
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Benefits
|
|
92
|
+
|
|
93
|
+
1. **Flexible field mapping**: Form fields don't need to match action properties 1:1
|
|
94
|
+
2. **Data consolidation**: Combine multiple action properties into single form fields
|
|
95
|
+
3. **Cleaner separation**: Form structure is independent of action structure
|
|
96
|
+
4. **Backward compatibility**: Existing configs continue to work unchanged
|
|
97
|
+
|
|
98
|
+
## Backward Compatibility
|
|
99
|
+
|
|
100
|
+
The ActionEditor automatically falls back to the old per-property approach when form-level transformations aren't provided:
|
|
101
|
+
|
|
102
|
+
- Uses `properties` if `form` is not defined
|
|
103
|
+
- Applies per-property `toFormValue`/`fromFormValue` if action-level ones aren't provided
|
|
104
|
+
- Maintains 1:1 mapping when no transformations are specified
|
|
105
|
+
|
|
106
|
+
## Testing
|
|
107
|
+
|
|
108
|
+
Test both transformation directions:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
it('should transform action to form data', async () => {
|
|
112
|
+
// Set action and verify formData structure
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should transform form data back to action on save', async () => {
|
|
116
|
+
// Modify formData and verify saved action structure
|
|
117
|
+
});
|
|
118
|
+
```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/events.ts"],"names":[],"mappings":"","sourcesContent":["import { Msg, ObjectReference, User } from './interfaces';\n\nexport interface EventGroup {\n type: string;\n events: ContactEvent[];\n open: boolean;\n}\n\nexport interface ContactEvent {\n type: string;\n created_on: string;\n created_by?: User;\n}\n\nexport interface ChannelEvent extends ContactEvent {\n channel_event_type: string;\n duration: number;\n\n event: {\n type: string;\n channel: { uuid: string; name: string };\n duration?: number;\n optin?: {\n uuid: string;\n name: string;\n };\n };\n}\n\nexport interface ContactLanguageChangedEvent extends ContactEvent {\n language: string;\n step_uuid: string;\n session_uuid: string;\n}\n\nexport interface OptinRequestedEvent extends ContactEvent {\n optin: {\n uuid: string;\n name: string;\n };\n}\n\nexport interface MsgEvent extends ContactEvent {\n msg: Msg;\n status: string;\n failed_reason?: string;\n failed_reason_display?: string;\n logs_url: string;\n
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/events.ts"],"names":[],"mappings":"","sourcesContent":["import { Msg, ObjectReference, User } from './interfaces';\n\nexport interface EventGroup {\n type: string;\n events: ContactEvent[];\n open: boolean;\n}\n\nexport interface ContactEvent {\n uuid?: string;\n type: string;\n created_on: string;\n created_by?: User;\n}\n\nexport interface ChannelEvent extends ContactEvent {\n channel_event_type: string;\n duration: number;\n\n event: {\n type: string;\n channel: { uuid: string; name: string };\n duration?: number;\n optin?: {\n uuid: string;\n name: string;\n };\n };\n}\n\nexport interface ContactLanguageChangedEvent extends ContactEvent {\n language: string;\n step_uuid: string;\n session_uuid: string;\n}\n\nexport interface OptinRequestedEvent extends ContactEvent {\n optin: {\n uuid: string;\n name: string;\n };\n}\n\nexport interface MsgEvent extends ContactEvent {\n msg: Msg;\n status: string;\n failed_reason?: string;\n failed_reason_display?: string;\n logs_url: string;\n recipient_count?: number;\n created_by?: User;\n optin?: ObjectReference;\n}\n\nexport interface FlowEvent extends ContactEvent {\n flow: ObjectReference;\n status: string;\n}\n\nexport interface URNsChangedEvent extends ContactEvent {\n urns: string[];\n}\n\nexport interface TicketEvent extends ContactEvent {\n note?: string;\n assignee?: User;\n ticket: {\n uuid: string;\n topic?: ObjectReference;\n closed_on?: string;\n opened_on?: string;\n };\n topic?: ObjectReference;\n created_by?: User;\n}\n\nexport interface NameChangedEvent extends ContactEvent {\n name: string;\n}\n\nexport interface UpdateFieldEvent extends ContactEvent {\n field: { key: string; name: string };\n value: { text: string };\n}\n\nexport interface ContactGroupsEvent extends ContactEvent {\n groups_added: ObjectReference[];\n groups_removed: ObjectReference[];\n}\n\nexport interface AirtimeTransferredEvent extends ContactEvent {\n sender: string;\n recipient: string;\n currency: string;\n desired_amount: string;\n actual_amount: string;\n}\n\nexport type CallStartedEvent = ContactEvent;\n\nexport interface ContactHistoryPage {\n has_older: boolean;\n recent_only: boolean;\n next_before: number;\n next_after: number;\n start_date: Date;\n events: ContactEvent[];\n}\n"]}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
2
|
import { css, html } from 'lit';
|
|
3
|
-
import {
|
|
3
|
+
import { repeat } from 'lit/directives/repeat.js';
|
|
4
|
+
import { ACTION_CONFIG, NODE_CONFIG } from './config';
|
|
4
5
|
import { property } from 'lit/decorators.js';
|
|
5
6
|
import { RapidElement } from '../RapidElement';
|
|
6
7
|
import { getClasses } from '../utils';
|
|
7
8
|
import { getStore } from '../store/Store';
|
|
8
9
|
import { CustomEventType } from '../interfaces';
|
|
9
|
-
|
|
10
|
+
const DRAG_THRESHOLD = 5;
|
|
11
|
+
export class CanvasNode extends RapidElement {
|
|
10
12
|
createRenderRoot() {
|
|
11
13
|
return this;
|
|
12
14
|
}
|
|
@@ -25,6 +27,11 @@ export class EditorNode extends RapidElement {
|
|
|
25
27
|
|
|
26
28
|
}
|
|
27
29
|
|
|
30
|
+
/* Cap width for execute_actions nodes */
|
|
31
|
+
.node.execute-actions {
|
|
32
|
+
max-width: 200px;
|
|
33
|
+
}
|
|
34
|
+
|
|
28
35
|
.node:hover {
|
|
29
36
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
|
30
37
|
}
|
|
@@ -36,8 +43,8 @@ export class EditorNode extends RapidElement {
|
|
|
36
43
|
}
|
|
37
44
|
|
|
38
45
|
.action {
|
|
39
|
-
max-width: 200px;
|
|
40
46
|
position: relative;
|
|
47
|
+
font-size: 13px;
|
|
41
48
|
}
|
|
42
49
|
|
|
43
50
|
.action .remove-button {
|
|
@@ -59,18 +66,40 @@ export class EditorNode extends RapidElement {
|
|
|
59
66
|
z-index: 10;
|
|
60
67
|
}
|
|
61
68
|
|
|
62
|
-
.action:hover .remove-button
|
|
69
|
+
.action:hover .remove-button,
|
|
70
|
+
.router:hover .remove-button {
|
|
63
71
|
display: flex;
|
|
64
72
|
}
|
|
65
73
|
|
|
66
|
-
.action.removing .title
|
|
74
|
+
.action.removing .title,
|
|
75
|
+
.router .title.removing {
|
|
67
76
|
background-color: var(--color-error, #dc3545) !important;
|
|
68
77
|
}
|
|
69
78
|
|
|
70
|
-
.action.removing .title .name
|
|
79
|
+
.action.removing .title .name,
|
|
80
|
+
.router .title.removing .name {
|
|
71
81
|
color: white;
|
|
72
82
|
}
|
|
73
83
|
|
|
84
|
+
.router .remove-button {
|
|
85
|
+
position: absolute;
|
|
86
|
+
top: 5px;
|
|
87
|
+
right: 5px;
|
|
88
|
+
width: 16px;
|
|
89
|
+
height: 16px;
|
|
90
|
+
border-radius: 50%;
|
|
91
|
+
background: var(--color-error, #dc3545);
|
|
92
|
+
color: white;
|
|
93
|
+
border: none;
|
|
94
|
+
cursor: pointer;
|
|
95
|
+
display: none;
|
|
96
|
+
align-items: center;
|
|
97
|
+
justify-content: center;
|
|
98
|
+
font-size: 10px;
|
|
99
|
+
line-height: 1;
|
|
100
|
+
z-index: 10;
|
|
101
|
+
}
|
|
102
|
+
|
|
74
103
|
.action.sortable {
|
|
75
104
|
display: flex;
|
|
76
105
|
align-items: stretch;
|
|
@@ -80,10 +109,17 @@ export class EditorNode extends RapidElement {
|
|
|
80
109
|
flex-grow: 1;
|
|
81
110
|
display: flex;
|
|
82
111
|
flex-direction: column;
|
|
112
|
+
min-width: 0; /* Allow flex item to shrink below its content size */
|
|
113
|
+
overflow: hidden;
|
|
83
114
|
}
|
|
84
115
|
|
|
85
116
|
.action .body {
|
|
86
|
-
padding:
|
|
117
|
+
padding: 0.75em;
|
|
118
|
+
word-wrap: break-word;
|
|
119
|
+
overflow-wrap: break-word;
|
|
120
|
+
hyphens: auto;
|
|
121
|
+
white-space: normal;
|
|
122
|
+
overflow: hidden;
|
|
87
123
|
}
|
|
88
124
|
|
|
89
125
|
.action .drag-handle {
|
|
@@ -159,6 +195,10 @@ export class EditorNode extends RapidElement {
|
|
|
159
195
|
.category .title {
|
|
160
196
|
font-weight: normal;
|
|
161
197
|
font-size: 1em;
|
|
198
|
+
max-width: 150px;
|
|
199
|
+
white-space: nowrap;
|
|
200
|
+
overflow: hidden;
|
|
201
|
+
text-overflow: ellipsis;
|
|
162
202
|
}
|
|
163
203
|
|
|
164
204
|
.router .body {
|
|
@@ -267,26 +307,39 @@ export class EditorNode extends RapidElement {
|
|
|
267
307
|
this.actionRemovalTimeouts = new Map();
|
|
268
308
|
// Set of action UUIDs that are in the removing state
|
|
269
309
|
this.actionRemovingState = new Set();
|
|
310
|
+
// Track action click state to distinguish from drag
|
|
311
|
+
this.actionClickStartPos = null;
|
|
312
|
+
this.pendingActionClick = null;
|
|
313
|
+
// Track node click state to distinguish from drag
|
|
314
|
+
this.nodeClickStartPos = null;
|
|
315
|
+
this.pendingNodeClick = null;
|
|
270
316
|
this.handleActionOrderChanged = this.handleActionOrderChanged.bind(this);
|
|
271
317
|
}
|
|
272
318
|
updated(changes) {
|
|
273
319
|
var _a;
|
|
274
320
|
super.updated(changes);
|
|
275
321
|
if (changes.has('node')) {
|
|
276
|
-
//
|
|
277
|
-
if (
|
|
322
|
+
// Only proceed if plumber is available (for tests that don't set it up)
|
|
323
|
+
if (this.plumber) {
|
|
324
|
+
this.plumber.removeNodeConnections(this.node.uuid);
|
|
325
|
+
// make our initial connections
|
|
278
326
|
for (const exit of this.node.exits) {
|
|
279
327
|
if (!exit.destination_uuid) {
|
|
328
|
+
// if we have no destination, then we are a source
|
|
329
|
+
// so make our source endpoint
|
|
280
330
|
this.plumber.makeSource(exit.uuid);
|
|
281
331
|
}
|
|
282
332
|
else {
|
|
283
333
|
this.plumber.connectIds(this.node.uuid, exit.uuid, exit.destination_uuid);
|
|
284
334
|
}
|
|
285
335
|
}
|
|
336
|
+
this.plumber.revalidate([this.node.uuid]);
|
|
286
337
|
}
|
|
287
338
|
const ele = this.parentElement;
|
|
288
|
-
|
|
289
|
-
|
|
339
|
+
if (ele) {
|
|
340
|
+
const rect = ele.getBoundingClientRect();
|
|
341
|
+
(_a = getStore()) === null || _a === void 0 ? void 0 : _a.getState().expandCanvas(this.ui.position.left + rect.width, this.ui.position.top + rect.height);
|
|
342
|
+
}
|
|
290
343
|
}
|
|
291
344
|
}
|
|
292
345
|
disconnectedCallback() {
|
|
@@ -412,6 +465,44 @@ export class EditorNode extends RapidElement {
|
|
|
412
465
|
this.requestUpdate();
|
|
413
466
|
}
|
|
414
467
|
}
|
|
468
|
+
handleNodeRemoveClick(event) {
|
|
469
|
+
event.preventDefault();
|
|
470
|
+
event.stopPropagation();
|
|
471
|
+
const nodeId = this.node.uuid;
|
|
472
|
+
// If the node is already in removing state, perform the removal
|
|
473
|
+
if (this.actionRemovingState.has(nodeId)) {
|
|
474
|
+
this.removeNode();
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
// Start removal UI state
|
|
478
|
+
this.actionRemovingState.add(nodeId);
|
|
479
|
+
this.requestUpdate();
|
|
480
|
+
// Clear any existing timeout for this node
|
|
481
|
+
if (this.actionRemovalTimeouts.has(nodeId)) {
|
|
482
|
+
clearTimeout(this.actionRemovalTimeouts.get(nodeId));
|
|
483
|
+
}
|
|
484
|
+
// Set timeout to reset UI if user doesn't click
|
|
485
|
+
const timeoutId = window.setTimeout(() => {
|
|
486
|
+
this.actionRemovingState.delete(nodeId);
|
|
487
|
+
this.actionRemovalTimeouts.delete(nodeId);
|
|
488
|
+
this.requestUpdate();
|
|
489
|
+
}, 1000); // 1 second as per requirements
|
|
490
|
+
this.actionRemovalTimeouts.set(nodeId, timeoutId);
|
|
491
|
+
}
|
|
492
|
+
removeNode() {
|
|
493
|
+
const nodeId = this.node.uuid;
|
|
494
|
+
// Clear the UI state
|
|
495
|
+
this.actionRemovingState.delete(nodeId);
|
|
496
|
+
// Clear any timeout
|
|
497
|
+
if (this.actionRemovalTimeouts.has(nodeId)) {
|
|
498
|
+
clearTimeout(this.actionRemovalTimeouts.get(nodeId));
|
|
499
|
+
this.actionRemovalTimeouts.delete(nodeId);
|
|
500
|
+
}
|
|
501
|
+
// Fire the node deleted event
|
|
502
|
+
this.fireCustomEvent(CustomEventType.NodeDeleted, {
|
|
503
|
+
uuid: this.node.uuid
|
|
504
|
+
});
|
|
505
|
+
}
|
|
415
506
|
handleActionOrderChanged(event) {
|
|
416
507
|
var _a;
|
|
417
508
|
const [fromIdx, toIdx] = event.detail.swap;
|
|
@@ -425,6 +516,162 @@ export class EditorNode extends RapidElement {
|
|
|
425
516
|
this.node = { ...this.node, actions: newActions };
|
|
426
517
|
(_a = getStore()) === null || _a === void 0 ? void 0 : _a.getState().updateNode(this.node.uuid, { ...this.node, actions: newActions });
|
|
427
518
|
}
|
|
519
|
+
handleActionMouseDown(event, action) {
|
|
520
|
+
// Don't handle clicks on the remove button or when action is in removing state
|
|
521
|
+
const target = event.target;
|
|
522
|
+
if (target.closest('.remove-button') ||
|
|
523
|
+
this.actionRemovingState.has(action.uuid)) {
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
// Store the starting position and action for later comparison
|
|
527
|
+
// Don't prevent default - let the Editor's drag system work normally
|
|
528
|
+
this.actionClickStartPos = { x: event.clientX, y: event.clientY };
|
|
529
|
+
this.pendingActionClick = { action, event };
|
|
530
|
+
}
|
|
531
|
+
handleActionMouseUp(event, action) {
|
|
532
|
+
// Don't handle if we don't have a pending click or if it's not the same action
|
|
533
|
+
if (!this.pendingActionClick ||
|
|
534
|
+
this.pendingActionClick.action.uuid !== action.uuid) {
|
|
535
|
+
this.actionClickStartPos = null;
|
|
536
|
+
this.pendingActionClick = null;
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
// Don't handle clicks on the remove button or when action is in removing state
|
|
540
|
+
const target = event.target;
|
|
541
|
+
if (target.closest('.remove-button') ||
|
|
542
|
+
this.actionRemovingState.has(action.uuid)) {
|
|
543
|
+
this.actionClickStartPos = null;
|
|
544
|
+
this.pendingActionClick = null;
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
// Check if the mouse moved beyond the drag threshold
|
|
548
|
+
if (this.actionClickStartPos) {
|
|
549
|
+
const deltaX = event.clientX - this.actionClickStartPos.x;
|
|
550
|
+
const deltaY = event.clientY - this.actionClickStartPos.y;
|
|
551
|
+
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
552
|
+
// Check if the Editor is currently in dragging mode
|
|
553
|
+
const editor = this.closest('temba-flow-editor');
|
|
554
|
+
const editorWasDragging = editor === null || editor === void 0 ? void 0 : editor.dragging;
|
|
555
|
+
// Only fire the action edit event if we haven't dragged beyond the threshold
|
|
556
|
+
// AND either there's no Editor parent (test case) or the Editor didn't drag the node
|
|
557
|
+
if (distance <= DRAG_THRESHOLD && (!editor || !editorWasDragging)) {
|
|
558
|
+
// Fire event to request action editing
|
|
559
|
+
this.fireCustomEvent(CustomEventType.ActionEditRequested, {
|
|
560
|
+
action,
|
|
561
|
+
nodeUuid: this.node.uuid
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
// Clean up
|
|
566
|
+
this.actionClickStartPos = null;
|
|
567
|
+
this.pendingActionClick = null;
|
|
568
|
+
}
|
|
569
|
+
handleActionClick(event, action) {
|
|
570
|
+
// This method is kept for backward compatibility but should not be used
|
|
571
|
+
// The new mousedown/mouseup approach handles click vs drag properly
|
|
572
|
+
event.preventDefault();
|
|
573
|
+
event.stopPropagation();
|
|
574
|
+
// Don't handle clicks on the remove button or when action is in removing state
|
|
575
|
+
const target = event.target;
|
|
576
|
+
if (target.closest('.remove-button') ||
|
|
577
|
+
this.actionRemovingState.has(action.uuid)) {
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
// Fire event to request action editing
|
|
581
|
+
this.fireCustomEvent(CustomEventType.ActionEditRequested, {
|
|
582
|
+
action,
|
|
583
|
+
nodeUuid: this.node.uuid
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
handleNodeEditClick(event) {
|
|
587
|
+
event.preventDefault();
|
|
588
|
+
event.stopPropagation();
|
|
589
|
+
// Don't handle clicks on the remove button or when node is in removing state
|
|
590
|
+
const target = event.target;
|
|
591
|
+
if (target.closest('.remove-button') ||
|
|
592
|
+
this.actionRemovingState.has(this.node.uuid)) {
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
// Fire node edit requested event if the node has a router
|
|
596
|
+
if (this.node.router) {
|
|
597
|
+
// If router node has exactly one action, open the action editor directly
|
|
598
|
+
if (this.node.actions && this.node.actions.length === 1) {
|
|
599
|
+
this.fireCustomEvent(CustomEventType.ActionEditRequested, {
|
|
600
|
+
action: this.node.actions[0],
|
|
601
|
+
nodeUuid: this.node.uuid
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
else {
|
|
605
|
+
// Otherwise open the node editor as before
|
|
606
|
+
this.fireCustomEvent(CustomEventType.NodeEditRequested, {
|
|
607
|
+
node: this.node,
|
|
608
|
+
nodeUI: this.ui
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
handleNodeMouseDown(event) {
|
|
614
|
+
// Don't handle clicks on the remove button or when node is in removing state
|
|
615
|
+
const target = event.target;
|
|
616
|
+
if (target.closest('.remove-button') ||
|
|
617
|
+
this.actionRemovingState.has(this.node.uuid)) {
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
// Store the starting position for later comparison
|
|
621
|
+
// Don't prevent default - let the Editor's drag system work normally
|
|
622
|
+
this.nodeClickStartPos = { x: event.clientX, y: event.clientY };
|
|
623
|
+
this.pendingNodeClick = { event };
|
|
624
|
+
}
|
|
625
|
+
handleNodeMouseUp(event) {
|
|
626
|
+
// Don't handle if we don't have a pending click
|
|
627
|
+
if (!this.pendingNodeClick) {
|
|
628
|
+
this.nodeClickStartPos = null;
|
|
629
|
+
this.pendingNodeClick = null;
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
// Don't handle clicks on the remove button or when node is in removing state
|
|
633
|
+
const target = event.target;
|
|
634
|
+
if (target.closest('.remove-button') ||
|
|
635
|
+
this.actionRemovingState.has(this.node.uuid)) {
|
|
636
|
+
this.nodeClickStartPos = null;
|
|
637
|
+
this.pendingNodeClick = null;
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
// Check if the mouse moved beyond the drag threshold
|
|
641
|
+
if (this.nodeClickStartPos) {
|
|
642
|
+
const deltaX = event.clientX - this.nodeClickStartPos.x;
|
|
643
|
+
const deltaY = event.clientY - this.nodeClickStartPos.y;
|
|
644
|
+
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
645
|
+
// Check if the Editor is currently in dragging mode
|
|
646
|
+
const editor = this.closest('temba-flow-editor');
|
|
647
|
+
const editorWasDragging = editor === null || editor === void 0 ? void 0 : editor.dragging;
|
|
648
|
+
// Only fire the node edit event if we haven't dragged beyond the threshold
|
|
649
|
+
// AND either there's no Editor parent (test case) or the Editor didn't drag the node
|
|
650
|
+
if (distance <= 5 && (!editor || !editorWasDragging)) {
|
|
651
|
+
// Using literal 5 instead of DRAG_THRESHOLD since it's not imported
|
|
652
|
+
// Fire event to request node editing if the node has a router
|
|
653
|
+
if (this.node.router) {
|
|
654
|
+
// If router node has exactly one action, open the action editor directly
|
|
655
|
+
if (this.node.actions && this.node.actions.length === 1) {
|
|
656
|
+
this.fireCustomEvent(CustomEventType.ActionEditRequested, {
|
|
657
|
+
action: this.node.actions[0],
|
|
658
|
+
nodeUuid: this.node.uuid
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
662
|
+
// Otherwise open the node editor as before
|
|
663
|
+
this.fireCustomEvent(CustomEventType.NodeEditRequested, {
|
|
664
|
+
node: this.node,
|
|
665
|
+
nodeUI: this.ui
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
// Clean up
|
|
672
|
+
this.nodeClickStartPos = null;
|
|
673
|
+
this.pendingNodeClick = null;
|
|
674
|
+
}
|
|
428
675
|
renderTitle(config, isRemoving = false) {
|
|
429
676
|
var _a, _b;
|
|
430
677
|
return html `<div class="title" style="background:${config.color}">
|
|
@@ -435,8 +682,16 @@ export class EditorNode extends RapidElement {
|
|
|
435
682
|
<div class="name">${isRemoving ? 'Remove?' : config.name}</div>
|
|
436
683
|
</div>`;
|
|
437
684
|
}
|
|
685
|
+
renderNodeTitle(config, isRemoving = false) {
|
|
686
|
+
return html `<div
|
|
687
|
+
class="title ${isRemoving ? 'removing' : ''}"
|
|
688
|
+
style="background:${config.color}"
|
|
689
|
+
>
|
|
690
|
+
<div class="name">${isRemoving ? 'Remove?' : config.name}</div>
|
|
691
|
+
</div>`;
|
|
692
|
+
}
|
|
438
693
|
renderAction(node, action, index) {
|
|
439
|
-
const config =
|
|
694
|
+
const config = ACTION_CONFIG[action.type];
|
|
440
695
|
const isRemoving = this.actionRemovingState.has(action.uuid);
|
|
441
696
|
if (config) {
|
|
442
697
|
return html `<div
|
|
@@ -450,7 +705,12 @@ export class EditorNode extends RapidElement {
|
|
|
450
705
|
>
|
|
451
706
|
✕
|
|
452
707
|
</button>
|
|
453
|
-
<div
|
|
708
|
+
<div
|
|
709
|
+
class="action-content"
|
|
710
|
+
@mousedown=${(e) => this.handleActionMouseDown(e, action)}
|
|
711
|
+
@mouseup=${(e) => this.handleActionMouseUp(e, action)}
|
|
712
|
+
style="cursor: pointer;"
|
|
713
|
+
>
|
|
454
714
|
${this.renderTitle(config, isRemoving)}
|
|
455
715
|
<div class="body">
|
|
456
716
|
${config.render
|
|
@@ -475,12 +735,37 @@ export class EditorNode extends RapidElement {
|
|
|
475
735
|
</div>`;
|
|
476
736
|
}
|
|
477
737
|
renderRouter(router, ui) {
|
|
478
|
-
const
|
|
479
|
-
if (
|
|
480
|
-
|
|
481
|
-
|
|
738
|
+
const nodeConfig = NODE_CONFIG[ui.type];
|
|
739
|
+
if (nodeConfig) {
|
|
740
|
+
// For tests that call renderRouter directly without setting this.node
|
|
741
|
+
const hasActions = this.node ? this.node.actions.length > 0 : false;
|
|
742
|
+
const isRemoving = this.node &&
|
|
743
|
+
this.node.actions.length === 0 &&
|
|
744
|
+
this.actionRemovingState.has(this.node.uuid);
|
|
745
|
+
return html `<div class="router" style="position: relative;">
|
|
746
|
+
${!hasActions
|
|
747
|
+
? html ` <button
|
|
748
|
+
class="remove-button"
|
|
749
|
+
@click=${(e) => this.handleNodeRemoveClick(e)}
|
|
750
|
+
title="Remove node"
|
|
751
|
+
>
|
|
752
|
+
✕
|
|
753
|
+
</button>
|
|
754
|
+
<div
|
|
755
|
+
@mousedown=${(e) => this.handleNodeMouseDown(e)}
|
|
756
|
+
@mouseup=${(e) => this.handleNodeMouseUp(e)}
|
|
757
|
+
style="cursor: pointer;"
|
|
758
|
+
>
|
|
759
|
+
${this.renderNodeTitle(nodeConfig, isRemoving)}
|
|
760
|
+
</div>`
|
|
761
|
+
: ''}
|
|
482
762
|
${router.result_name
|
|
483
|
-
? html `<div
|
|
763
|
+
? html `<div
|
|
764
|
+
class="body"
|
|
765
|
+
@mousedown=${(e) => this.handleNodeMouseDown(e)}
|
|
766
|
+
@mouseup=${(e) => this.handleNodeMouseUp(e)}
|
|
767
|
+
style="cursor: pointer;"
|
|
768
|
+
>
|
|
484
769
|
Save as
|
|
485
770
|
<div class="result-name">${router.result_name}</div>
|
|
486
771
|
</div>`
|
|
@@ -492,14 +777,20 @@ export class EditorNode extends RapidElement {
|
|
|
492
777
|
if (!node.router || !node.router.categories) {
|
|
493
778
|
return null;
|
|
494
779
|
}
|
|
495
|
-
|
|
780
|
+
return html `<div class="categories">
|
|
781
|
+
${repeat(node.router.categories, (category) => category.uuid, (category) => {
|
|
496
782
|
const exit = node.exits.find((exit) => exit.uuid == category.exit_uuid);
|
|
497
|
-
return html `<div
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
783
|
+
return html `<div
|
|
784
|
+
class="category"
|
|
785
|
+
@mousedown=${(e) => this.handleNodeMouseDown(e)}
|
|
786
|
+
@mouseup=${(e) => this.handleNodeMouseUp(e)}
|
|
787
|
+
style="cursor: pointer;"
|
|
788
|
+
>
|
|
789
|
+
<div class="title">${category.name}</div>
|
|
790
|
+
${this.renderExit(exit)}
|
|
791
|
+
</div>`;
|
|
792
|
+
})}
|
|
793
|
+
</div>`;
|
|
503
794
|
}
|
|
504
795
|
renderExit(exit) {
|
|
505
796
|
return html `<div class="exit-wrapper">
|
|
@@ -518,29 +809,41 @@ export class EditorNode extends RapidElement {
|
|
|
518
809
|
if (!this.node || !this.ui) {
|
|
519
810
|
return html `<div class="node">Loading...</div>`;
|
|
520
811
|
}
|
|
812
|
+
const nodeConfig = NODE_CONFIG[this.ui.type];
|
|
521
813
|
return html `
|
|
522
814
|
<div
|
|
523
815
|
id="${this.node.uuid}"
|
|
524
|
-
class="node
|
|
816
|
+
class="node ${this.ui.type === 'execute_actions'
|
|
817
|
+
? 'execute-actions'
|
|
818
|
+
: ''}"
|
|
525
819
|
style="left:${this.ui.position.left}px;top:${this.ui.position.top}px"
|
|
526
820
|
>
|
|
527
821
|
${this.node.actions.length > 0
|
|
528
|
-
?
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
:
|
|
822
|
+
? this.ui.type === 'execute_actions'
|
|
823
|
+
? html `<temba-sortable-list
|
|
824
|
+
dragHandle="drag-handle"
|
|
825
|
+
@temba-order-changed="${this.handleActionOrderChanged}"
|
|
826
|
+
>
|
|
827
|
+
${repeat(this.node.actions, (action) => action.uuid, (action, index) => this.renderAction(this.node, action, index))}
|
|
828
|
+
</temba-sortable-list>`
|
|
829
|
+
: html `${repeat(this.node.actions, (action) => action.uuid, (action, index) => this.renderAction(this.node, action, index))}`
|
|
830
|
+
: !this.node.router && nodeConfig && nodeConfig.name
|
|
831
|
+
? html `<div class="router" style="position: relative;">
|
|
832
|
+
<button
|
|
833
|
+
class="remove-button"
|
|
834
|
+
@click=${(e) => this.handleNodeRemoveClick(e)}
|
|
835
|
+
title="Remove node"
|
|
836
|
+
>
|
|
837
|
+
✕
|
|
838
|
+
</button>
|
|
839
|
+
${this.renderNodeTitle(nodeConfig, this.actionRemovingState.has(this.node.uuid))}
|
|
840
|
+
</div>`
|
|
841
|
+
: ''}
|
|
537
842
|
${this.node.router
|
|
538
843
|
? html ` ${this.renderRouter(this.node.router, this.ui)}
|
|
539
844
|
${this.renderCategories(this.node)}`
|
|
540
845
|
: html `<div class="action-exits">
|
|
541
|
-
${this.node.exits.
|
|
542
|
-
return this.renderExit(exit);
|
|
543
|
-
})}
|
|
846
|
+
${repeat(this.node.exits, (exit) => exit.uuid, (exit) => this.renderExit(exit))}
|
|
544
847
|
</div>`}
|
|
545
848
|
</div>
|
|
546
849
|
`;
|
|
@@ -548,11 +851,11 @@ export class EditorNode extends RapidElement {
|
|
|
548
851
|
}
|
|
549
852
|
__decorate([
|
|
550
853
|
property({ type: Object })
|
|
551
|
-
],
|
|
854
|
+
], CanvasNode.prototype, "plumber", void 0);
|
|
552
855
|
__decorate([
|
|
553
856
|
property({ type: Object })
|
|
554
|
-
],
|
|
857
|
+
], CanvasNode.prototype, "node", void 0);
|
|
555
858
|
__decorate([
|
|
556
859
|
property({ type: Object })
|
|
557
|
-
],
|
|
558
|
-
//# sourceMappingURL=
|
|
860
|
+
], CanvasNode.prototype, "ui", void 0);
|
|
861
|
+
//# sourceMappingURL=CanvasNode.js.map
|