@nyaruka/temba-components 0.132.0 → 0.134.0
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/CHANGELOG.md +31 -1
- package/demo/components/flow/example.html +1 -0
- package/demo/components/webchat/example.html +1 -1
- package/demo/static/css/tailwind.css +30019 -0
- package/dist/locales/es.js +5 -5
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/fr.js +5 -5
- package/dist/locales/fr.js.map +1 -1
- package/dist/locales/locale-codes.js +2 -11
- package/dist/locales/locale-codes.js.map +1 -1
- package/dist/locales/pt.js +5 -5
- package/dist/locales/pt.js.map +1 -1
- package/dist/temba-components.js +555 -476
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/display/Chat.js +248 -95
- package/out-tsc/src/display/Chat.js.map +1 -1
- package/out-tsc/src/display/FloatingTab.js +4 -4
- package/out-tsc/src/display/FloatingTab.js.map +1 -1
- package/out-tsc/src/display/TembaUser.js +3 -3
- package/out-tsc/src/display/TembaUser.js.map +1 -1
- package/out-tsc/src/events.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +132 -58
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +183 -58
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/utils.js +141 -0
- package/out-tsc/src/flow/utils.js.map +1 -1
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/layout/FloatingWindow.js +1 -2
- package/out-tsc/src/layout/FloatingWindow.js.map +1 -1
- package/out-tsc/src/list/ContentMenu.js +1 -0
- package/out-tsc/src/list/ContentMenu.js.map +1 -1
- package/out-tsc/src/list/SortableList.js +3 -2
- package/out-tsc/src/list/SortableList.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +184 -205
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/locales/es.js +5 -5
- package/out-tsc/src/locales/es.js.map +1 -1
- package/out-tsc/src/locales/fr.js +5 -5
- package/out-tsc/src/locales/fr.js.map +1 -1
- package/out-tsc/src/locales/locale-codes.js +2 -11
- package/out-tsc/src/locales/locale-codes.js.map +1 -1
- package/out-tsc/src/locales/pt.js +5 -5
- package/out-tsc/src/locales/pt.js.map +1 -1
- package/out-tsc/src/store/AppState.js +34 -0
- package/out-tsc/src/store/AppState.js.map +1 -1
- package/out-tsc/src/store/Store.js +5 -5
- package/out-tsc/src/store/Store.js.map +1 -1
- package/out-tsc/src/utils.js +3 -3
- package/out-tsc/src/utils.js.map +1 -1
- package/out-tsc/src/webchat/WebChat.js +22 -9
- package/out-tsc/src/webchat/WebChat.js.map +1 -1
- package/out-tsc/test/ActionHelper.js +6 -5
- package/out-tsc/test/ActionHelper.js.map +1 -1
- package/out-tsc/test/actions/send_broadcast.test.js +9 -4
- package/out-tsc/test/actions/send_broadcast.test.js.map +1 -1
- package/out-tsc/test/temba-contact-chat.test.js +1 -1
- package/out-tsc/test/temba-contact-chat.test.js.map +1 -1
- package/out-tsc/test/temba-floating-window.test.js +0 -2
- package/out-tsc/test/temba-floating-window.test.js.map +1 -1
- package/out-tsc/test/temba-flow-collision.test.js +673 -0
- package/out-tsc/test/temba-flow-collision.test.js.map +1 -0
- package/out-tsc/test/temba-flow-editor-node.test.js +195 -0
- package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
- package/out-tsc/test/temba-utils-uuid.test.js +45 -1
- package/out-tsc/test/temba-utils-uuid.test.js.map +1 -1
- package/out-tsc/test/utils.test.js +2 -2
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +1 -1
- 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/add_contact_urn/render/expression-facebook.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/expression-phone.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/facebook-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/instagram-handle.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/line-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/phone-number.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/telegram-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/viber-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/wechat-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/whatsapp.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_broadcast/render/contacts-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/groups-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/many-groups.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/multiline-text.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/with-attachments.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/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/actions/start_session/render/contact-query.png +0 -0
- package/screenshots/truth/actions/start_session/render/contacts-only.png +0 -0
- package/screenshots/truth/actions/start_session/render/create-contact.png +0 -0
- package/screenshots/truth/actions/start_session/render/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/start_session/render/groups-only.png +0 -0
- package/screenshots/truth/actions/start_session/render/many-recipients.png +0 -0
- package/screenshots/truth/contacts/chat-failure.png +0 -0
- package/screenshots/truth/contacts/chat-for-archived-contact.png +0 -0
- package/screenshots/truth/contacts/chat-for-blocked-contact.png +0 -0
- package/screenshots/truth/contacts/chat-for-stopped-contact.png +0 -0
- package/screenshots/truth/contacts/chat-sends-attachments-only.png +0 -0
- package/screenshots/truth/contacts/chat-sends-text-and-attachments.png +0 -0
- package/screenshots/truth/contacts/chat-sends-text-only.png +0 -0
- package/screenshots/truth/floating-tab/default.png +0 -0
- package/screenshots/truth/floating-tab/gray.png +0 -0
- package/screenshots/truth/floating-tab/green.png +0 -0
- package/screenshots/truth/floating-tab/hover.png +0 -0
- package/screenshots/truth/floating-tab/purple.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
- package/src/display/Chat.ts +331 -135
- package/src/display/FloatingTab.ts +4 -4
- package/src/display/TembaUser.ts +3 -2
- package/src/events.ts +12 -12
- package/src/flow/CanvasNode.ts +140 -57
- package/src/flow/Editor.ts +240 -58
- package/src/flow/utils.ts +207 -1
- package/src/interfaces.ts +7 -0
- package/src/layout/FloatingWindow.ts +1 -3
- package/src/list/ContentMenu.ts +1 -0
- package/src/list/SortableList.ts +3 -2
- package/src/live/ContactChat.ts +195 -221
- package/src/locales/es.ts +13 -18
- package/src/locales/fr.ts +13 -18
- package/src/locales/locale-codes.ts +2 -11
- package/src/locales/pt.ts +13 -18
- package/src/store/AppState.ts +43 -0
- package/src/store/Store.ts +5 -5
- package/src/utils.ts +3 -3
- package/src/webchat/WebChat.ts +24 -10
- package/test/ActionHelper.ts +13 -5
- package/test/actions/send_broadcast.test.ts +4 -2
- package/test/temba-contact-chat.test.ts +1 -1
- package/test/temba-floating-window.test.ts +0 -2
- package/test/temba-flow-collision.test.ts +833 -0
- package/test/temba-flow-editor-node.test.ts +224 -0
- package/test/temba-utils-uuid.test.ts +61 -1
- package/test/utils.test.ts +7 -2
- package/test-assets/contacts/history.json +22 -9
- package/web-test-runner.config.mjs +3 -3
|
@@ -13,7 +13,7 @@ export class FloatingTab extends RapidElement {
|
|
|
13
13
|
.tab {
|
|
14
14
|
position: fixed;
|
|
15
15
|
right: 0;
|
|
16
|
-
z-index:
|
|
16
|
+
z-index: 4998;
|
|
17
17
|
transition: transform var(--transition-duration, 300ms) ease-in-out;
|
|
18
18
|
display: flex;
|
|
19
19
|
align-items: center;
|
|
@@ -36,8 +36,8 @@ export class FloatingTab extends RapidElement {
|
|
|
36
36
|
display: flex;
|
|
37
37
|
align-items: center;
|
|
38
38
|
justify-content: center;
|
|
39
|
-
width:
|
|
40
|
-
height:
|
|
39
|
+
width: 16px;
|
|
40
|
+
height: 16px;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
temba-icon {
|
|
@@ -164,7 +164,7 @@ export class FloatingTab extends RapidElement {
|
|
|
164
164
|
<div class="${classes}" style="${tabStyle}" @click=${this.handleClick}>
|
|
165
165
|
<div class="icon-container">
|
|
166
166
|
${this.icon
|
|
167
|
-
? html`<temba-icon size="
|
|
167
|
+
? html`<temba-icon size="1.5" name="${this.icon}"></temba-icon>`
|
|
168
168
|
: ''}
|
|
169
169
|
</div>
|
|
170
170
|
<div class="label">${this.label}</div>
|
package/src/display/TembaUser.ts
CHANGED
|
@@ -62,6 +62,9 @@ export class TembaUser extends RapidElement {
|
|
|
62
62
|
@property({ type: String })
|
|
63
63
|
email: string;
|
|
64
64
|
|
|
65
|
+
@property({ type: String })
|
|
66
|
+
uuid: string;
|
|
67
|
+
|
|
65
68
|
@property({ type: String })
|
|
66
69
|
avatar: string;
|
|
67
70
|
|
|
@@ -87,8 +90,6 @@ export class TembaUser extends RapidElement {
|
|
|
87
90
|
if (changed.has('avatar')) {
|
|
88
91
|
if (this.avatar) {
|
|
89
92
|
this.bgimage = `url('${this.avatar}') center / contain no-repeat`;
|
|
90
|
-
} else if (!this.system) {
|
|
91
|
-
this.bgimage = null;
|
|
92
93
|
}
|
|
93
94
|
}
|
|
94
95
|
}
|
package/src/events.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Msg, ObjectReference, User } from './interfaces';
|
|
2
|
+
import { ContactEvent } from './display/Chat';
|
|
2
3
|
|
|
3
4
|
export interface EventGroup {
|
|
4
5
|
type: string;
|
|
@@ -6,13 +7,6 @@ export interface EventGroup {
|
|
|
6
7
|
open: boolean;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
export interface ContactEvent {
|
|
10
|
-
uuid?: string;
|
|
11
|
-
type: string;
|
|
12
|
-
created_on: string;
|
|
13
|
-
_user?: ObjectReference;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
10
|
export interface ChannelEvent extends ContactEvent {
|
|
17
11
|
channel_event_type: string;
|
|
18
12
|
duration: number;
|
|
@@ -57,7 +51,16 @@ export interface ChatStartedEvent extends ContactEvent {
|
|
|
57
51
|
export interface MsgEvent extends ContactEvent {
|
|
58
52
|
msg: Msg;
|
|
59
53
|
optin?: ObjectReference;
|
|
60
|
-
_status?: {
|
|
54
|
+
_status?: {
|
|
55
|
+
created_on: string;
|
|
56
|
+
status: 'wired' | 'sent' | 'delivered' | 'read' | 'errored' | 'failed';
|
|
57
|
+
reason: 'error_limit' | 'too_old' | 'channel_removed';
|
|
58
|
+
};
|
|
59
|
+
_deleted?: {
|
|
60
|
+
created_on: string;
|
|
61
|
+
by_contact: boolean;
|
|
62
|
+
user: { name: string; uuid: string };
|
|
63
|
+
};
|
|
61
64
|
_logs_url?: string;
|
|
62
65
|
}
|
|
63
66
|
|
|
@@ -107,8 +110,5 @@ export type CallStartedEvent = ContactEvent;
|
|
|
107
110
|
|
|
108
111
|
export interface ContactHistoryPage {
|
|
109
112
|
events: ContactEvent[];
|
|
110
|
-
|
|
111
|
-
recent_only: boolean;
|
|
112
|
-
next_before: number;
|
|
113
|
-
next_after: number;
|
|
113
|
+
next: string | null;
|
|
114
114
|
}
|
package/src/flow/CanvasNode.ts
CHANGED
|
@@ -76,6 +76,10 @@ export class CanvasNode extends RapidElement {
|
|
|
76
76
|
actionHeight: number;
|
|
77
77
|
} | null = null;
|
|
78
78
|
|
|
79
|
+
// Track if we're showing a placeholder for our own last action being dragged out
|
|
80
|
+
private showLastActionPlaceholder = false;
|
|
81
|
+
private lastActionPlaceholderHeight = 60;
|
|
82
|
+
|
|
79
83
|
static get styles() {
|
|
80
84
|
return css`
|
|
81
85
|
|
|
@@ -119,6 +123,7 @@ export class CanvasNode extends RapidElement {
|
|
|
119
123
|
|
|
120
124
|
.action .cn-title:hover .remove-button,
|
|
121
125
|
.router:hover .remove-button {
|
|
126
|
+
visibility: visible;
|
|
122
127
|
opacity: 0.7;
|
|
123
128
|
}
|
|
124
129
|
|
|
@@ -135,7 +140,7 @@ export class CanvasNode extends RapidElement {
|
|
|
135
140
|
.remove-button {
|
|
136
141
|
background: transparent;
|
|
137
142
|
color: white;
|
|
138
|
-
|
|
143
|
+
visibility: hidden;
|
|
139
144
|
cursor: pointer;
|
|
140
145
|
font-size: 1em;
|
|
141
146
|
font-weight: 600;
|
|
@@ -143,16 +148,22 @@ export class CanvasNode extends RapidElement {
|
|
|
143
148
|
z-index: 10;
|
|
144
149
|
transition: all 100ms ease-in-out;
|
|
145
150
|
align-self: center;
|
|
146
|
-
|
|
151
|
+
margin-right:0.15em;
|
|
147
152
|
border: 0px solid red;
|
|
148
153
|
width: 1em;
|
|
149
154
|
pointer-events: auto; /* Ensure remove button can receive events */
|
|
150
155
|
}
|
|
151
156
|
|
|
152
157
|
.remove-button:hover {
|
|
158
|
+
visibility: visible;
|
|
153
159
|
opacity: 1;
|
|
154
160
|
}
|
|
155
161
|
|
|
162
|
+
.translating-hidden {
|
|
163
|
+
visibility: hidden !important;
|
|
164
|
+
pointer-events: none !important;
|
|
165
|
+
}
|
|
166
|
+
|
|
156
167
|
.action.sortable {
|
|
157
168
|
display: flex;
|
|
158
169
|
align-items: stretch;
|
|
@@ -164,6 +175,7 @@ export class CanvasNode extends RapidElement {
|
|
|
164
175
|
flex-direction: column;
|
|
165
176
|
min-width: 0; /* Allow flex item to shrink below its content size */
|
|
166
177
|
overflow: hidden;
|
|
178
|
+
background: #fff;
|
|
167
179
|
}
|
|
168
180
|
|
|
169
181
|
.action .body {
|
|
@@ -194,7 +206,7 @@ export class CanvasNode extends RapidElement {
|
|
|
194
206
|
}
|
|
195
207
|
|
|
196
208
|
.action .drag-handle {
|
|
197
|
-
|
|
209
|
+
visibility: hidden;
|
|
198
210
|
transition: all 200ms ease-in-out;
|
|
199
211
|
cursor: move;
|
|
200
212
|
background: rgba(0, 0, 0, 0.02);
|
|
@@ -209,6 +221,7 @@ export class CanvasNode extends RapidElement {
|
|
|
209
221
|
}
|
|
210
222
|
|
|
211
223
|
.action:hover .drag-handle {
|
|
224
|
+
visibility: visible;
|
|
212
225
|
opacity: 0.7;
|
|
213
226
|
|
|
214
227
|
|
|
@@ -219,6 +232,7 @@ export class CanvasNode extends RapidElement {
|
|
|
219
232
|
}
|
|
220
233
|
|
|
221
234
|
.action .drag-handle:hover {
|
|
235
|
+
visibility: visible;
|
|
222
236
|
opacity: 1;
|
|
223
237
|
|
|
224
238
|
}
|
|
@@ -422,6 +436,18 @@ export class CanvasNode extends RapidElement {
|
|
|
422
436
|
opacity: 1 !important;
|
|
423
437
|
transform: scale(1.1);
|
|
424
438
|
}
|
|
439
|
+
|
|
440
|
+
.empty-node-placeholder {
|
|
441
|
+
height: 60px;
|
|
442
|
+
background: #f3f4f6;
|
|
443
|
+
border: 2px dashed #d1d5db;
|
|
444
|
+
border-radius: var(--curvature);
|
|
445
|
+
display: flex;
|
|
446
|
+
align-items: center;
|
|
447
|
+
justify-content: center;
|
|
448
|
+
color: #9ca3af;
|
|
449
|
+
font-size: 0.9em;
|
|
450
|
+
}
|
|
425
451
|
}`;
|
|
426
452
|
}
|
|
427
453
|
|
|
@@ -471,6 +497,16 @@ export class CanvasNode extends RapidElement {
|
|
|
471
497
|
changes: PropertyValueMap<any> | Map<PropertyKey, unknown>
|
|
472
498
|
): void {
|
|
473
499
|
super.updated(changes);
|
|
500
|
+
|
|
501
|
+
if (!!changes.get('ui') && changes.has('ui')) {
|
|
502
|
+
// run revalidation every 50ms until 350ms to catch animation updates
|
|
503
|
+
for (let delay = 25; delay <= 350; delay += 25) {
|
|
504
|
+
setTimeout(() => {
|
|
505
|
+
this.plumber.revalidate([this.node.uuid]);
|
|
506
|
+
}, delay);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
474
510
|
if (changes.has('node')) {
|
|
475
511
|
// Only proceed if plumber is available (for tests that don't set it up)
|
|
476
512
|
if (this.plumber) {
|
|
@@ -734,6 +770,8 @@ export class CanvasNode extends RapidElement {
|
|
|
734
770
|
}
|
|
735
771
|
|
|
736
772
|
// Fire the node deleted event
|
|
773
|
+
// The Editor will handle cleanup (Plumber connections) and call store.removeNodes()
|
|
774
|
+
// The store's removeNodes method handles rerouting of connections
|
|
737
775
|
this.fireCustomEvent(CustomEventType.NodeDeleted, {
|
|
738
776
|
uuid: this.node.uuid
|
|
739
777
|
});
|
|
@@ -742,6 +780,12 @@ export class CanvasNode extends RapidElement {
|
|
|
742
780
|
private handleActionOrderChanged(event: CustomEvent) {
|
|
743
781
|
const [fromIdx, toIdx] = event.detail.swap;
|
|
744
782
|
|
|
783
|
+
// If we have an external drag in progress, ignore internal order changes
|
|
784
|
+
// as they'll be handled by the external drop handler
|
|
785
|
+
if (this.externalDragInfo) {
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
|
|
745
789
|
// swap our actions
|
|
746
790
|
const newActions = [...this.node.actions];
|
|
747
791
|
const movedAction = newActions.splice(fromIdx, 1)[0];
|
|
@@ -769,6 +813,13 @@ export class CanvasNode extends RapidElement {
|
|
|
769
813
|
// Fallback to a reasonable default
|
|
770
814
|
this.draggedActionHeight = 60;
|
|
771
815
|
}
|
|
816
|
+
|
|
817
|
+
// If this is the last action, show placeholder
|
|
818
|
+
if (this.node.actions.length === 1) {
|
|
819
|
+
this.showLastActionPlaceholder = true;
|
|
820
|
+
this.lastActionPlaceholderHeight = this.draggedActionHeight;
|
|
821
|
+
this.requestUpdate();
|
|
822
|
+
}
|
|
772
823
|
}
|
|
773
824
|
|
|
774
825
|
private handleActionDragExternal(event: CustomEvent) {
|
|
@@ -785,6 +836,9 @@ export class CanvasNode extends RapidElement {
|
|
|
785
836
|
const actionIndex = parseInt(splitId[1], 10);
|
|
786
837
|
const action = this.node.actions[actionIndex];
|
|
787
838
|
|
|
839
|
+
// Check if this is the last action
|
|
840
|
+
const isLastAction = this.node.actions.length === 1;
|
|
841
|
+
|
|
788
842
|
// fire event to editor to show canvas drop preview, including the captured height
|
|
789
843
|
this.fireCustomEvent(CustomEventType.DragExternal, {
|
|
790
844
|
action,
|
|
@@ -792,7 +846,8 @@ export class CanvasNode extends RapidElement {
|
|
|
792
846
|
actionIndex,
|
|
793
847
|
mouseX: event.detail.mouseX,
|
|
794
848
|
mouseY: event.detail.mouseY,
|
|
795
|
-
actionHeight: this.draggedActionHeight
|
|
849
|
+
actionHeight: this.draggedActionHeight,
|
|
850
|
+
isLastAction
|
|
796
851
|
});
|
|
797
852
|
}
|
|
798
853
|
|
|
@@ -807,6 +862,9 @@ export class CanvasNode extends RapidElement {
|
|
|
807
862
|
private handleActionDragStop(event: CustomEvent) {
|
|
808
863
|
const isExternal = event.detail.isExternal;
|
|
809
864
|
|
|
865
|
+
// Clear last action placeholder when drag stops
|
|
866
|
+
this.showLastActionPlaceholder = false;
|
|
867
|
+
|
|
810
868
|
if (isExternal) {
|
|
811
869
|
// stop propagation of the original event from SortableList
|
|
812
870
|
event.stopPropagation();
|
|
@@ -821,16 +879,23 @@ export class CanvasNode extends RapidElement {
|
|
|
821
879
|
const actionIndex = parseInt(split[1], 10);
|
|
822
880
|
const action = this.node.actions[actionIndex];
|
|
823
881
|
|
|
824
|
-
//
|
|
882
|
+
// Check if this is the last action in the node
|
|
883
|
+
const isLastAction = this.node.actions.length === 1;
|
|
884
|
+
|
|
885
|
+
// Always fire the DragStop event so the Editor can handle drops on other nodes
|
|
886
|
+
// The Editor will decide whether to create a new node or drop on existing node
|
|
825
887
|
this.fireCustomEvent(CustomEventType.DragStop, {
|
|
826
888
|
action,
|
|
827
889
|
nodeUuid: this.node.uuid,
|
|
828
890
|
actionIndex,
|
|
829
891
|
isExternal: true,
|
|
892
|
+
isLastAction,
|
|
830
893
|
mouseX: event.detail.mouseX,
|
|
831
894
|
mouseY: event.detail.mouseY
|
|
832
895
|
});
|
|
833
896
|
}
|
|
897
|
+
|
|
898
|
+
this.requestUpdate();
|
|
834
899
|
}
|
|
835
900
|
|
|
836
901
|
private handleActionMouseDown(event: MouseEvent, action: Action): void {
|
|
@@ -1141,7 +1206,6 @@ export class CanvasNode extends RapidElement {
|
|
|
1141
1206
|
// Clear external drag state
|
|
1142
1207
|
this.externalDragInfo = null;
|
|
1143
1208
|
|
|
1144
|
-
// Remove the action from the source node
|
|
1145
1209
|
const store = getStore();
|
|
1146
1210
|
if (!store) return;
|
|
1147
1211
|
|
|
@@ -1152,33 +1216,36 @@ export class CanvasNode extends RapidElement {
|
|
|
1152
1216
|
(n) => n.uuid === sourceNodeUuid
|
|
1153
1217
|
);
|
|
1154
1218
|
|
|
1155
|
-
if (sourceNode)
|
|
1156
|
-
const updatedSourceActions = sourceNode.actions.filter(
|
|
1157
|
-
(_a, idx) => idx !== actionIndex
|
|
1158
|
-
);
|
|
1159
|
-
|
|
1160
|
-
// If source node has no actions left, remove it
|
|
1161
|
-
if (updatedSourceActions.length === 0) {
|
|
1162
|
-
this.fireCustomEvent(CustomEventType.NodeDeleted, {
|
|
1163
|
-
uuid: sourceNodeUuid
|
|
1164
|
-
});
|
|
1165
|
-
} else {
|
|
1166
|
-
// Update source node
|
|
1167
|
-
const updatedSourceNode = {
|
|
1168
|
-
...sourceNode,
|
|
1169
|
-
actions: updatedSourceActions
|
|
1170
|
-
};
|
|
1171
|
-
getStore()?.getState().updateNode(sourceNodeUuid, updatedSourceNode);
|
|
1172
|
-
}
|
|
1173
|
-
}
|
|
1219
|
+
if (!sourceNode) return;
|
|
1174
1220
|
|
|
1175
|
-
// Add the action to this node
|
|
1221
|
+
// IMPORTANT: Add the action to this node FIRST, before removing from source
|
|
1222
|
+
// This ensures we don't lose the action if the source node gets deleted
|
|
1176
1223
|
const newActions = [...this.node.actions];
|
|
1177
1224
|
newActions.splice(dropIndex, 0, action);
|
|
1178
1225
|
|
|
1179
1226
|
const updatedNode = { ...this.node, actions: newActions };
|
|
1180
1227
|
getStore()?.getState().updateNode(this.node.uuid, updatedNode);
|
|
1181
1228
|
|
|
1229
|
+
// Now remove the action from the source node
|
|
1230
|
+
const updatedSourceActions = sourceNode.actions.filter(
|
|
1231
|
+
(_a, idx) => idx !== actionIndex
|
|
1232
|
+
);
|
|
1233
|
+
|
|
1234
|
+
// If source node has no actions left, remove it
|
|
1235
|
+
if (updatedSourceActions.length === 0) {
|
|
1236
|
+
// Fire event to Editor so it can clean up jsPlumb connections properly
|
|
1237
|
+
this.fireCustomEvent(CustomEventType.NodeDeleted, {
|
|
1238
|
+
uuid: sourceNodeUuid
|
|
1239
|
+
});
|
|
1240
|
+
} else {
|
|
1241
|
+
// Update source node
|
|
1242
|
+
const updatedSourceNode = {
|
|
1243
|
+
...sourceNode,
|
|
1244
|
+
actions: updatedSourceActions
|
|
1245
|
+
};
|
|
1246
|
+
getStore()?.getState().updateNode(sourceNodeUuid, updatedSourceNode);
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1182
1249
|
// Request update
|
|
1183
1250
|
this.requestUpdate();
|
|
1184
1251
|
}
|
|
@@ -1193,21 +1260,31 @@ export class CanvasNode extends RapidElement {
|
|
|
1193
1260
|
? ACTION_GROUP_METADATA[config.group]?.color
|
|
1194
1261
|
: '#aaaaaa';
|
|
1195
1262
|
return html`<div class="cn-title" style="background:${color}">
|
|
1196
|
-
${
|
|
1197
|
-
? html`<temba-icon
|
|
1263
|
+
${this.ui?.type === 'execute_actions'
|
|
1264
|
+
? html`<temba-icon
|
|
1265
|
+
class="drag-handle ${this.isTranslating
|
|
1266
|
+
? 'translating-hidden'
|
|
1267
|
+
: ''}"
|
|
1268
|
+
name="sort"
|
|
1269
|
+
></temba-icon>`
|
|
1270
|
+
: this.node?.actions?.length > 1
|
|
1271
|
+
? html`<temba-icon
|
|
1272
|
+
class="drag-handle ${this.isTranslating
|
|
1273
|
+
? 'translating-hidden'
|
|
1274
|
+
: ''}"
|
|
1275
|
+
name="sort"
|
|
1276
|
+
></temba-icon>`
|
|
1198
1277
|
: html`<div class="title-spacer"></div>`}
|
|
1199
1278
|
|
|
1200
1279
|
<div class="name">${isRemoving ? 'Remove?' : config.name}</div>
|
|
1201
|
-
|
|
1202
|
-
?
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
</div>`
|
|
1210
|
-
: html`<div class="title-spacer"></div>`}
|
|
1280
|
+
<div
|
|
1281
|
+
class="remove-button ${this.isTranslating ? 'translating-hidden' : ''}"
|
|
1282
|
+
@click=${(e: MouseEvent) =>
|
|
1283
|
+
this.handleActionRemoveClick(e, action, index)}
|
|
1284
|
+
title="Remove action"
|
|
1285
|
+
>
|
|
1286
|
+
✕
|
|
1287
|
+
</div>
|
|
1211
1288
|
</div>`;
|
|
1212
1289
|
}
|
|
1213
1290
|
|
|
@@ -1234,15 +1311,13 @@ export class CanvasNode extends RapidElement {
|
|
|
1234
1311
|
? config.renderTitle(node, ui)
|
|
1235
1312
|
: html`${config.name}`}
|
|
1236
1313
|
</div>
|
|
1237
|
-
|
|
1238
|
-
?
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
</div>`
|
|
1245
|
-
: html`<div class="title-spacer"></div>`}
|
|
1314
|
+
<div
|
|
1315
|
+
class="remove-button ${this.isTranslating ? 'translating-hidden' : ''}"
|
|
1316
|
+
@click=${(e: MouseEvent) => this.handleNodeRemoveClick(e)}
|
|
1317
|
+
title="Remove node"
|
|
1318
|
+
>
|
|
1319
|
+
✕
|
|
1320
|
+
</div>
|
|
1246
1321
|
</div>`;
|
|
1247
1322
|
}
|
|
1248
1323
|
|
|
@@ -1528,19 +1603,27 @@ export class CanvasNode extends RapidElement {
|
|
|
1528
1603
|
: this.node.actions.length > 0
|
|
1529
1604
|
? this.ui.type === 'execute_actions'
|
|
1530
1605
|
? html`<temba-sortable-list
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1606
|
+
dragHandle="drag-handle"
|
|
1607
|
+
externalDrag
|
|
1608
|
+
@temba-order-changed="${this.handleActionOrderChanged}"
|
|
1609
|
+
@temba-drag-start="${this.handleActionDragStart}"
|
|
1610
|
+
@temba-drag-external="${this.handleActionDragExternal}"
|
|
1611
|
+
@temba-drag-internal="${this.handleActionDragInternal}"
|
|
1612
|
+
@temba-drag-stop="${this.handleActionDragStop}"
|
|
1613
|
+
>
|
|
1614
|
+
${this.renderActionsWithPlaceholder()}
|
|
1615
|
+
</temba-sortable-list>
|
|
1616
|
+
${this.showLastActionPlaceholder
|
|
1617
|
+
? html`<div
|
|
1618
|
+
class="empty-node-placeholder"
|
|
1619
|
+
style="height: ${this.lastActionPlaceholderHeight}px;"
|
|
1620
|
+
></div>`
|
|
1621
|
+
: ''}`
|
|
1541
1622
|
: html`${this.node.actions.map((action, index) =>
|
|
1542
1623
|
this.renderAction(this.node, action, index)
|
|
1543
1624
|
)}`
|
|
1625
|
+
: this.ui.type === 'execute_actions'
|
|
1626
|
+
? html`<div class="empty-node-placeholder"></div>`
|
|
1544
1627
|
: ''}
|
|
1545
1628
|
${this.node.router
|
|
1546
1629
|
? html`<div class="router-section">
|