@nyaruka/temba-components 0.142.1 → 0.142.3
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 +19 -0
- package/dist/temba-components.js +953 -708
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/Icons.js +1 -0
- package/out-tsc/src/Icons.js.map +1 -1
- package/out-tsc/src/flow/CanvasMenu.js +38 -38
- package/out-tsc/src/flow/CanvasMenu.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +171 -17
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +491 -22
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +346 -10
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/NodeTypeSelector.js +2 -0
- package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -1
- package/out-tsc/src/flow/Plumber.js +92 -28
- package/out-tsc/src/flow/Plumber.js.map +1 -1
- package/out-tsc/src/flow/StickyNote.js +63 -3
- package/out-tsc/src/flow/StickyNote.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_urn.js +2 -6
- package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
- package/out-tsc/src/flow/actions/enter_flow.js +2 -2
- package/out-tsc/src/flow/actions/enter_flow.js.map +1 -1
- package/out-tsc/src/flow/actions/say_msg.js +2 -1
- package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
- package/out-tsc/src/flow/actions/send_broadcast.js +2 -6
- package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
- package/out-tsc/src/flow/actions/send_email.js +2 -6
- package/out-tsc/src/flow/actions/send_email.js.map +1 -1
- package/out-tsc/src/flow/actions/send_msg.js +55 -35
- package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_channel.js +2 -1
- package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_field.js +4 -5
- package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_language.js +3 -3
- package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_name.js +2 -1
- package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_status.js +2 -1
- package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
- package/out-tsc/src/flow/actions/set_run_result.js +3 -3
- package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
- package/out-tsc/src/flow/actions/start_session.js +2 -2
- package/out-tsc/src/flow/actions/start_session.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm.js +4 -5
- package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_resthook.js +3 -8
- package/out-tsc/src/flow/nodes/split_by_resthook.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_subflow.js +4 -2
- package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_webhook.js +25 -33
- package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js +1 -0
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/flow/types.js.map +1 -1
- package/out-tsc/src/flow/utils.js +66 -0
- package/out-tsc/src/flow/utils.js.map +1 -1
- package/out-tsc/src/form/FieldRenderer.js +17 -2
- package/out-tsc/src/form/FieldRenderer.js.map +1 -1
- package/out-tsc/src/interfaces.js +1 -0
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/list/SortableList.js +104 -43
- package/out-tsc/src/list/SortableList.js.map +1 -1
- package/out-tsc/src/simulator/Simulator.js +6 -2
- package/out-tsc/src/simulator/Simulator.js.map +1 -1
- package/out-tsc/test/temba-canvas-menu.test.js +13 -9
- package/out-tsc/test/temba-canvas-menu.test.js.map +1 -1
- package/out-tsc/test/temba-flow-reflow.test.js.map +1 -1
- package/out-tsc/test/temba-node-editor.test.js +9 -10
- package/out-tsc/test/temba-node-editor.test.js.map +1 -1
- package/out-tsc/test/temba-node-type-selector.test.js +3 -3
- package/out-tsc/test/temba-node-type-selector.test.js.map +1 -1
- package/out-tsc/test/temba-simulator.test.js +2 -2
- package/out-tsc/test/temba-simulator.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/actions/enter_flow/render/basic-flow.png +0 -0
- package/screenshots/truth/actions/enter_flow/render/long-flow-name.png +0 -0
- package/screenshots/truth/actions/send_email/render/long-subject.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/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/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/canvas-menu/open.png +0 -0
- package/screenshots/truth/node-type-selector/action-mode.png +0 -0
- package/screenshots/truth/node-type-selector/split-mode.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_categorize/editor/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
- package/src/Icons.ts +1 -0
- package/src/flow/CanvasMenu.ts +50 -43
- package/src/flow/CanvasNode.ts +201 -17
- package/src/flow/Editor.ts +585 -25
- package/src/flow/NodeEditor.ts +373 -10
- package/src/flow/NodeTypeSelector.ts +2 -0
- package/src/flow/Plumber.ts +104 -37
- package/src/flow/StickyNote.ts +76 -4
- package/src/flow/actions/add_contact_urn.ts +5 -6
- package/src/flow/actions/enter_flow.ts +2 -2
- package/src/flow/actions/say_msg.ts +2 -1
- package/src/flow/actions/send_broadcast.ts +2 -6
- package/src/flow/actions/send_email.ts +2 -6
- package/src/flow/actions/send_msg.ts +59 -38
- package/src/flow/actions/set_contact_channel.ts +5 -1
- package/src/flow/actions/set_contact_field.ts +10 -5
- package/src/flow/actions/set_contact_language.ts +6 -3
- package/src/flow/actions/set_contact_name.ts +5 -1
- package/src/flow/actions/set_contact_status.ts +5 -1
- package/src/flow/actions/set_run_result.ts +6 -3
- package/src/flow/actions/start_session.ts +2 -2
- package/src/flow/nodes/split_by_llm.ts +5 -5
- package/src/flow/nodes/split_by_resthook.ts +3 -8
- package/src/flow/nodes/split_by_subflow.ts +4 -2
- package/src/flow/nodes/split_by_webhook.ts +26 -34
- package/src/flow/nodes/wait_for_response.ts +1 -0
- package/src/flow/types.ts +25 -2
- package/src/flow/utils.ts +79 -1
- package/src/form/FieldRenderer.ts +32 -3
- package/src/interfaces.ts +1 -0
- package/src/list/SortableList.ts +117 -47
- package/src/simulator/Simulator.ts +6 -2
- package/test/temba-canvas-menu.test.ts +13 -9
- package/test/temba-flow-reflow.test.ts +4 -2
- package/test/temba-node-editor.test.ts +9 -10
- package/test/temba-node-type-selector.test.ts +3 -3
- package/test/temba-simulator.test.ts +2 -2
package/src/flow/CanvasMenu.ts
CHANGED
|
@@ -7,7 +7,13 @@ import { CustomEventType } from '../interfaces';
|
|
|
7
7
|
* Event detail for canvas menu selection
|
|
8
8
|
*/
|
|
9
9
|
export interface CanvasMenuSelection {
|
|
10
|
-
action:
|
|
10
|
+
action:
|
|
11
|
+
| 'sticky'
|
|
12
|
+
| 'action'
|
|
13
|
+
| 'split'
|
|
14
|
+
| 'send_msg'
|
|
15
|
+
| 'wait_for_response'
|
|
16
|
+
| 'reflow';
|
|
11
17
|
position: { x: number; y: number };
|
|
12
18
|
}
|
|
13
19
|
|
|
@@ -50,13 +56,6 @@ export class CanvasMenu extends RapidElement {
|
|
|
50
56
|
|
|
51
57
|
.menu-item temba-icon {
|
|
52
58
|
--icon-color: var(--color-text);
|
|
53
|
-
margin-top: 0.15em;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
.menu-item-content {
|
|
57
|
-
display: flex;
|
|
58
|
-
flex-direction: column;
|
|
59
|
-
gap: 0.15em;
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
.menu-item-title {
|
|
@@ -65,11 +64,6 @@ export class CanvasMenu extends RapidElement {
|
|
|
65
64
|
color: var(--color-text-dark);
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
.menu-item-description {
|
|
69
|
-
font-size: 0.85rem;
|
|
70
|
-
color: var(--color-text);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
67
|
.divider {
|
|
74
68
|
height: 1px;
|
|
75
69
|
background: rgba(0, 0, 0, 0.1);
|
|
@@ -90,6 +84,9 @@ export class CanvasMenu extends RapidElement {
|
|
|
90
84
|
@property({ type: Boolean })
|
|
91
85
|
public showStickyNote = true;
|
|
92
86
|
|
|
87
|
+
@property({ type: Boolean })
|
|
88
|
+
public showWaitForResponse = true;
|
|
89
|
+
|
|
93
90
|
@property({ type: Boolean })
|
|
94
91
|
public showReflow = false;
|
|
95
92
|
|
|
@@ -101,16 +98,20 @@ export class CanvasMenu extends RapidElement {
|
|
|
101
98
|
): void {
|
|
102
99
|
super.firstUpdated(_changedProperties);
|
|
103
100
|
|
|
104
|
-
// Close menu when clicking outside — use mousedown instead of
|
|
105
|
-
// to avoid being triggered by the click synthesized from a
|
|
106
|
-
// (mousedown on exit + mouseup on canvas = click on
|
|
107
|
-
|
|
101
|
+
// Close menu when clicking/tapping outside — use mousedown instead of
|
|
102
|
+
// click to avoid being triggered by the click synthesized from a
|
|
103
|
+
// drag-and-drop (mousedown on exit + mouseup on canvas = click on
|
|
104
|
+
// common ancestor). Also listen for touchstart for touch devices.
|
|
105
|
+
const handleClickOutside = (e: MouseEvent | TouchEvent) => {
|
|
108
106
|
if (this.open && !this.contains(e.target as Node)) {
|
|
109
107
|
this.close();
|
|
110
108
|
}
|
|
111
109
|
};
|
|
112
110
|
|
|
113
111
|
document.addEventListener('mousedown', handleClickOutside);
|
|
112
|
+
document.addEventListener('touchstart', handleClickOutside, {
|
|
113
|
+
passive: true
|
|
114
|
+
});
|
|
114
115
|
|
|
115
116
|
// Store cleanup function
|
|
116
117
|
(this as any)._clickOutsideHandler = handleClickOutside;
|
|
@@ -123,6 +124,10 @@ export class CanvasMenu extends RapidElement {
|
|
|
123
124
|
'mousedown',
|
|
124
125
|
(this as any)._clickOutsideHandler
|
|
125
126
|
);
|
|
127
|
+
document.removeEventListener(
|
|
128
|
+
'touchstart',
|
|
129
|
+
(this as any)._clickOutsideHandler
|
|
130
|
+
);
|
|
126
131
|
}
|
|
127
132
|
}
|
|
128
133
|
|
|
@@ -131,13 +136,15 @@ export class CanvasMenu extends RapidElement {
|
|
|
131
136
|
y: number,
|
|
132
137
|
clickPosition: { x: number; y: number },
|
|
133
138
|
showStickyNote: boolean = true,
|
|
134
|
-
showReflow: boolean = false
|
|
139
|
+
showReflow: boolean = false,
|
|
140
|
+
showWaitForResponse: boolean = true
|
|
135
141
|
) {
|
|
136
142
|
this.x = x;
|
|
137
143
|
this.y = y;
|
|
138
144
|
this.clickPosition = clickPosition;
|
|
139
145
|
this.showStickyNote = showStickyNote;
|
|
140
146
|
this.showReflow = showReflow;
|
|
147
|
+
this.showWaitForResponse = showWaitForResponse;
|
|
141
148
|
this.open = true;
|
|
142
149
|
|
|
143
150
|
// Adjust position after menu renders to ensure it fits on screen
|
|
@@ -185,9 +192,7 @@ export class CanvasMenu extends RapidElement {
|
|
|
185
192
|
}
|
|
186
193
|
}
|
|
187
194
|
|
|
188
|
-
private handleMenuItemClick(
|
|
189
|
-
action: 'sticky' | 'action' | 'split' | 'reflow'
|
|
190
|
-
) {
|
|
195
|
+
private handleMenuItemClick(action: CanvasMenuSelection['action']) {
|
|
191
196
|
this.fireCustomEvent(CustomEventType.Selection, {
|
|
192
197
|
action,
|
|
193
198
|
position: this.clickPosition
|
|
@@ -203,17 +208,32 @@ export class CanvasMenu extends RapidElement {
|
|
|
203
208
|
|
|
204
209
|
return html`
|
|
205
210
|
<div class="menu" style="left: ${this.x}px; top: ${this.y}px;">
|
|
211
|
+
<div
|
|
212
|
+
class="menu-item"
|
|
213
|
+
@click=${() => this.handleMenuItemClick('send_msg')}
|
|
214
|
+
>
|
|
215
|
+
<temba-icon name="send" size="1.25"></temba-icon>
|
|
216
|
+
<div class="menu-item-title">Send Message</div>
|
|
217
|
+
</div>
|
|
218
|
+
|
|
219
|
+
${this.showWaitForResponse
|
|
220
|
+
? html`
|
|
221
|
+
<div
|
|
222
|
+
class="menu-item"
|
|
223
|
+
@click=${() => this.handleMenuItemClick('wait_for_response')}
|
|
224
|
+
>
|
|
225
|
+
<temba-icon name="message" size="1.25"></temba-icon>
|
|
226
|
+
<div class="menu-item-title">Wait for Response</div>
|
|
227
|
+
</div>
|
|
228
|
+
`
|
|
229
|
+
: ''}
|
|
230
|
+
|
|
206
231
|
<div
|
|
207
232
|
class="menu-item"
|
|
208
233
|
@click=${() => this.handleMenuItemClick('action')}
|
|
209
234
|
>
|
|
210
235
|
<temba-icon name="action" size="1.25"></temba-icon>
|
|
211
|
-
<div class="menu-item-
|
|
212
|
-
<div class="menu-item-title">Add Action</div>
|
|
213
|
-
<div class="menu-item-description">
|
|
214
|
-
Send messages, update contacts
|
|
215
|
-
</div>
|
|
216
|
-
</div>
|
|
236
|
+
<div class="menu-item-title">Add Action</div>
|
|
217
237
|
</div>
|
|
218
238
|
|
|
219
239
|
<div
|
|
@@ -221,10 +241,7 @@ export class CanvasMenu extends RapidElement {
|
|
|
221
241
|
@click=${() => this.handleMenuItemClick('split')}
|
|
222
242
|
>
|
|
223
243
|
<temba-icon name="split" size="1.25"></temba-icon>
|
|
224
|
-
<div class="menu-item-
|
|
225
|
-
<div class="menu-item-title">Add Split</div>
|
|
226
|
-
<div class="menu-item-description">Branch based on conditions</div>
|
|
227
|
-
</div>
|
|
244
|
+
<div class="menu-item-title">Add Split</div>
|
|
228
245
|
</div>
|
|
229
246
|
|
|
230
247
|
${this.showStickyNote
|
|
@@ -236,12 +253,7 @@ export class CanvasMenu extends RapidElement {
|
|
|
236
253
|
@click=${() => this.handleMenuItemClick('sticky')}
|
|
237
254
|
>
|
|
238
255
|
<temba-icon name="note" size="1.25"></temba-icon>
|
|
239
|
-
<div class="menu-item-
|
|
240
|
-
<div class="menu-item-title">Add Sticky Note</div>
|
|
241
|
-
<div class="menu-item-description">
|
|
242
|
-
Add a note to the canvas
|
|
243
|
-
</div>
|
|
244
|
-
</div>
|
|
256
|
+
<div class="menu-item-title">Add Sticky Note</div>
|
|
245
257
|
</div>
|
|
246
258
|
`
|
|
247
259
|
: ''}
|
|
@@ -254,12 +266,7 @@ export class CanvasMenu extends RapidElement {
|
|
|
254
266
|
@click=${() => this.handleMenuItemClick('reflow')}
|
|
255
267
|
>
|
|
256
268
|
<temba-icon name="flow" size="1.25"></temba-icon>
|
|
257
|
-
<div class="menu-item-
|
|
258
|
-
<div class="menu-item-title">Reflow</div>
|
|
259
|
-
<div class="menu-item-description">
|
|
260
|
-
Auto-arrange nodes in this flow
|
|
261
|
-
</div>
|
|
262
|
-
</div>
|
|
269
|
+
<div class="menu-item-title">Reflow</div>
|
|
263
270
|
</div>
|
|
264
271
|
`
|
|
265
272
|
: ''}
|
package/src/flow/CanvasNode.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { Action, Exit, Node, NodeUI, Router } from '../store/flow-definition';
|
|
|
6
6
|
import { property } from 'lit/decorators.js';
|
|
7
7
|
import { RapidElement } from '../RapidElement';
|
|
8
8
|
import { getClasses } from '../utils';
|
|
9
|
-
import { isRightClick } from './utils';
|
|
9
|
+
import { isRightClick, renderClamped } from './utils';
|
|
10
10
|
import { Plumber } from './Plumber';
|
|
11
11
|
import { getStore } from '../store/Store';
|
|
12
12
|
import { CustomEventType } from '../interfaces';
|
|
@@ -251,24 +251,20 @@ export class CanvasNode extends RapidElement {
|
|
|
251
251
|
}
|
|
252
252
|
.title-spacer {
|
|
253
253
|
width: 1.8em;
|
|
254
|
-
|
|
255
254
|
}
|
|
256
255
|
|
|
257
256
|
.action:hover .drag-handle {
|
|
258
257
|
visibility: visible;
|
|
259
258
|
opacity: 0.7;
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
strong {
|
|
265
|
-
font-weight: 500;
|
|
266
259
|
}
|
|
267
260
|
|
|
268
261
|
.action .drag-handle:hover {
|
|
269
262
|
visibility: visible;
|
|
270
263
|
opacity: 1;
|
|
271
|
-
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
strong {
|
|
267
|
+
font-weight: 500;
|
|
272
268
|
}
|
|
273
269
|
|
|
274
270
|
.action .cn-title,
|
|
@@ -295,6 +291,8 @@ export class CanvasNode extends RapidElement {
|
|
|
295
291
|
|
|
296
292
|
.quick-replies {
|
|
297
293
|
margin-top: 0.5em;
|
|
294
|
+
display: flex;
|
|
295
|
+
flex-wrap: wrap;
|
|
298
296
|
}
|
|
299
297
|
|
|
300
298
|
.quick-reply {
|
|
@@ -302,9 +300,14 @@ export class CanvasNode extends RapidElement {
|
|
|
302
300
|
border: 1px solid #e0e0e0;
|
|
303
301
|
border-radius: calc(var(--curvature) * 1.5);
|
|
304
302
|
padding: 0.2em 1em;
|
|
305
|
-
display: inline-block;
|
|
306
303
|
font-size: 0.8em;
|
|
307
304
|
margin: 0.2em;
|
|
305
|
+
flex: 0 1 auto;
|
|
306
|
+
min-width: 0;
|
|
307
|
+
max-width: 100%;
|
|
308
|
+
overflow: hidden;
|
|
309
|
+
text-overflow: ellipsis;
|
|
310
|
+
white-space: nowrap;
|
|
308
311
|
}
|
|
309
312
|
|
|
310
313
|
.router-section {
|
|
@@ -349,6 +352,7 @@ export class CanvasNode extends RapidElement {
|
|
|
349
352
|
|
|
350
353
|
.router .body {
|
|
351
354
|
padding: 0.75em;
|
|
355
|
+
max-width: 180px;
|
|
352
356
|
}
|
|
353
357
|
|
|
354
358
|
.result-name {
|
|
@@ -488,6 +492,22 @@ export class CanvasNode extends RapidElement {
|
|
|
488
492
|
color: #9ca3af;
|
|
489
493
|
font-size: 0.9em;
|
|
490
494
|
}
|
|
495
|
+
|
|
496
|
+
/* On touch devices, always show interactive controls.
|
|
497
|
+
The .touch-device class is added to the editor on first touch. */
|
|
498
|
+
.touch-device .remove-button {
|
|
499
|
+
visibility: visible !important;
|
|
500
|
+
opacity: 0.7;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.touch-device .action .drag-handle {
|
|
504
|
+
visibility: visible !important;
|
|
505
|
+
opacity: 0.7;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.touch-device .add-action-button {
|
|
509
|
+
opacity: 0.8 !important;
|
|
510
|
+
}
|
|
491
511
|
}`;
|
|
492
512
|
}
|
|
493
513
|
|
|
@@ -973,11 +993,12 @@ export class CanvasNode extends RapidElement {
|
|
|
973
993
|
private handleActionMouseDown(event: MouseEvent, action: Action): void {
|
|
974
994
|
if (isRightClick(event)) return;
|
|
975
995
|
|
|
976
|
-
// Don't handle clicks on the remove button, drag handle, or when action is in removing state
|
|
996
|
+
// Don't handle clicks on the remove button, drag handle, linked elements, or when action is in removing state
|
|
977
997
|
const target = event.target as HTMLElement;
|
|
978
998
|
if (
|
|
979
999
|
target.closest('.remove-button') ||
|
|
980
1000
|
target.closest('.drag-handle') ||
|
|
1001
|
+
target.closest('.linked-name') ||
|
|
981
1002
|
this.actionRemovingState.has(action.uuid)
|
|
982
1003
|
) {
|
|
983
1004
|
return;
|
|
@@ -1000,11 +1021,12 @@ export class CanvasNode extends RapidElement {
|
|
|
1000
1021
|
return;
|
|
1001
1022
|
}
|
|
1002
1023
|
|
|
1003
|
-
// Don't handle clicks on the remove button, drag handle, or when action is in removing state
|
|
1024
|
+
// Don't handle clicks on the remove button, drag handle, linked elements, or when action is in removing state
|
|
1004
1025
|
const target = event.target as HTMLElement;
|
|
1005
1026
|
if (
|
|
1006
1027
|
target.closest('.remove-button') ||
|
|
1007
1028
|
target.closest('.drag-handle') ||
|
|
1029
|
+
target.closest('.linked-name') ||
|
|
1008
1030
|
this.actionRemovingState.has(action.uuid)
|
|
1009
1031
|
) {
|
|
1010
1032
|
this.actionClickStartPos = null;
|
|
@@ -1046,6 +1068,75 @@ export class CanvasNode extends RapidElement {
|
|
|
1046
1068
|
this.pendingActionClick = null;
|
|
1047
1069
|
}
|
|
1048
1070
|
|
|
1071
|
+
/* c8 ignore start -- touch-only handlers untestable in headless Chromium */
|
|
1072
|
+
private handleActionTouchStart(event: TouchEvent, action: Action): void {
|
|
1073
|
+
const target = event.target as HTMLElement;
|
|
1074
|
+
if (
|
|
1075
|
+
target.closest('.remove-button') ||
|
|
1076
|
+
target.closest('.drag-handle') ||
|
|
1077
|
+
target.closest('.linked-name') ||
|
|
1078
|
+
this.actionRemovingState.has(action.uuid)
|
|
1079
|
+
) {
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
const touch = event.touches[0];
|
|
1084
|
+
if (!touch) return;
|
|
1085
|
+
this.actionClickStartPos = { x: touch.clientX, y: touch.clientY };
|
|
1086
|
+
this.pendingActionClick = { action, event: event as any };
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
private handleActionTouchEnd(event: TouchEvent, action: Action): void {
|
|
1090
|
+
if (
|
|
1091
|
+
!this.pendingActionClick ||
|
|
1092
|
+
this.pendingActionClick.action.uuid !== action.uuid
|
|
1093
|
+
) {
|
|
1094
|
+
this.actionClickStartPos = null;
|
|
1095
|
+
this.pendingActionClick = null;
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
const target = event.target as HTMLElement;
|
|
1100
|
+
if (
|
|
1101
|
+
target.closest('.remove-button') ||
|
|
1102
|
+
target.closest('.drag-handle') ||
|
|
1103
|
+
target.closest('.linked-name') ||
|
|
1104
|
+
this.actionRemovingState.has(action.uuid)
|
|
1105
|
+
) {
|
|
1106
|
+
this.actionClickStartPos = null;
|
|
1107
|
+
this.pendingActionClick = null;
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
const touch = event.changedTouches[0];
|
|
1112
|
+
if (this.actionClickStartPos && touch) {
|
|
1113
|
+
const deltaX = touch.clientX - this.actionClickStartPos.x;
|
|
1114
|
+
const deltaY = touch.clientY - this.actionClickStartPos.y;
|
|
1115
|
+
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
1116
|
+
|
|
1117
|
+
const editor = this.closest('temba-flow-editor') as any;
|
|
1118
|
+
const editorWasDragging = editor?.dragging;
|
|
1119
|
+
|
|
1120
|
+
if (distance <= DRAG_THRESHOLD && (!editor || !editorWasDragging)) {
|
|
1121
|
+
const actionEl = event.currentTarget as Element;
|
|
1122
|
+
const origin = actionEl
|
|
1123
|
+
? this.getTopCenter(actionEl)
|
|
1124
|
+
: { x: touch.clientX, y: touch.clientY };
|
|
1125
|
+
|
|
1126
|
+
this.fireCustomEvent(CustomEventType.ActionEditRequested, {
|
|
1127
|
+
action,
|
|
1128
|
+
nodeUuid: this.node.uuid,
|
|
1129
|
+
originX: origin.x,
|
|
1130
|
+
originY: origin.y
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
this.actionClickStartPos = null;
|
|
1136
|
+
this.pendingActionClick = null;
|
|
1137
|
+
}
|
|
1138
|
+
/* c8 ignore stop */
|
|
1139
|
+
|
|
1049
1140
|
private handleActionClick(event: MouseEvent, action: Action): void {
|
|
1050
1141
|
// This method is kept for backward compatibility but should not be used
|
|
1051
1142
|
// The new mousedown/mouseup approach handles click vs drag properly
|
|
@@ -1102,13 +1193,14 @@ export class CanvasNode extends RapidElement {
|
|
|
1102
1193
|
private handleNodeMouseDown(event: MouseEvent): void {
|
|
1103
1194
|
if (isRightClick(event)) return;
|
|
1104
1195
|
|
|
1105
|
-
// Don't handle clicks on the remove button, exits, drag handle, or when node is in removing state
|
|
1196
|
+
// Don't handle clicks on the remove button, exits, drag handle, linked elements, or when node is in removing state
|
|
1106
1197
|
const target = event.target as HTMLElement;
|
|
1107
1198
|
if (
|
|
1108
1199
|
target.closest('.remove-button') ||
|
|
1109
1200
|
target.closest('.exit') ||
|
|
1110
1201
|
target.closest('.exit-wrapper') ||
|
|
1111
1202
|
target.closest('.drag-handle') ||
|
|
1203
|
+
target.closest('.linked-name') ||
|
|
1112
1204
|
this.actionRemovingState.has(this.node.uuid)
|
|
1113
1205
|
) {
|
|
1114
1206
|
return;
|
|
@@ -1128,13 +1220,14 @@ export class CanvasNode extends RapidElement {
|
|
|
1128
1220
|
return;
|
|
1129
1221
|
}
|
|
1130
1222
|
|
|
1131
|
-
// Don't handle clicks on the remove button, exits, drag handle, or when node is in removing state
|
|
1223
|
+
// Don't handle clicks on the remove button, exits, drag handle, linked elements, or when node is in removing state
|
|
1132
1224
|
const target = event.target as HTMLElement;
|
|
1133
1225
|
if (
|
|
1134
1226
|
target.closest('.remove-button') ||
|
|
1135
1227
|
target.closest('.exit') ||
|
|
1136
1228
|
target.closest('.exit-wrapper') ||
|
|
1137
1229
|
target.closest('.drag-handle') ||
|
|
1230
|
+
target.closest('.linked-name') ||
|
|
1138
1231
|
this.actionRemovingState.has(this.node.uuid)
|
|
1139
1232
|
) {
|
|
1140
1233
|
this.nodeClickStartPos = null;
|
|
@@ -1187,6 +1280,84 @@ export class CanvasNode extends RapidElement {
|
|
|
1187
1280
|
this.pendingNodeClick = null;
|
|
1188
1281
|
}
|
|
1189
1282
|
|
|
1283
|
+
/* c8 ignore start -- touch-only handlers */
|
|
1284
|
+
private handleNodeTouchStart(event: TouchEvent): void {
|
|
1285
|
+
const target = event.target as HTMLElement;
|
|
1286
|
+
if (
|
|
1287
|
+
target.closest('.remove-button') ||
|
|
1288
|
+
target.closest('.exit') ||
|
|
1289
|
+
target.closest('.exit-wrapper') ||
|
|
1290
|
+
target.closest('.drag-handle') ||
|
|
1291
|
+
target.closest('.linked-name') ||
|
|
1292
|
+
this.actionRemovingState.has(this.node.uuid)
|
|
1293
|
+
) {
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
const touch = event.touches[0];
|
|
1298
|
+
if (!touch) return;
|
|
1299
|
+
this.nodeClickStartPos = { x: touch.clientX, y: touch.clientY };
|
|
1300
|
+
this.pendingNodeClick = { event: event as any };
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
private handleNodeTouchEnd(event: TouchEvent): void {
|
|
1304
|
+
if (!this.pendingNodeClick) {
|
|
1305
|
+
this.nodeClickStartPos = null;
|
|
1306
|
+
this.pendingNodeClick = null;
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
const target = event.target as HTMLElement;
|
|
1311
|
+
if (
|
|
1312
|
+
target.closest('.remove-button') ||
|
|
1313
|
+
target.closest('.exit') ||
|
|
1314
|
+
target.closest('.exit-wrapper') ||
|
|
1315
|
+
target.closest('.drag-handle') ||
|
|
1316
|
+
target.closest('.linked-name') ||
|
|
1317
|
+
this.actionRemovingState.has(this.node.uuid)
|
|
1318
|
+
) {
|
|
1319
|
+
this.nodeClickStartPos = null;
|
|
1320
|
+
this.pendingNodeClick = null;
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
const touch = event.changedTouches[0];
|
|
1325
|
+
if (this.nodeClickStartPos && touch) {
|
|
1326
|
+
const deltaX = touch.clientX - this.nodeClickStartPos.x;
|
|
1327
|
+
const deltaY = touch.clientY - this.nodeClickStartPos.y;
|
|
1328
|
+
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
1329
|
+
|
|
1330
|
+
const editor = this.closest('temba-flow-editor') as any;
|
|
1331
|
+
const editorWasDragging = editor?.dragging;
|
|
1332
|
+
|
|
1333
|
+
if (distance <= 5 && (!editor || !editorWasDragging)) {
|
|
1334
|
+
if (this.node.router) {
|
|
1335
|
+
const origin = this.getTopCenter(this);
|
|
1336
|
+
|
|
1337
|
+
if (this.node.actions && this.node.actions.length === 1) {
|
|
1338
|
+
this.fireCustomEvent(CustomEventType.ActionEditRequested, {
|
|
1339
|
+
action: this.node.actions[0],
|
|
1340
|
+
nodeUuid: this.node.uuid,
|
|
1341
|
+
originX: origin.x,
|
|
1342
|
+
originY: origin.y
|
|
1343
|
+
});
|
|
1344
|
+
} else {
|
|
1345
|
+
this.fireCustomEvent(CustomEventType.NodeEditRequested, {
|
|
1346
|
+
node: this.node,
|
|
1347
|
+
nodeUI: this.ui,
|
|
1348
|
+
originX: origin.x,
|
|
1349
|
+
originY: origin.y
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
this.nodeClickStartPos = null;
|
|
1357
|
+
this.pendingNodeClick = null;
|
|
1358
|
+
}
|
|
1359
|
+
/* c8 ignore stop */
|
|
1360
|
+
|
|
1190
1361
|
private handleAddActionClick(event: MouseEvent): void {
|
|
1191
1362
|
event.preventDefault();
|
|
1192
1363
|
event.stopPropagation();
|
|
@@ -1498,6 +1669,10 @@ export class CanvasNode extends RapidElement {
|
|
|
1498
1669
|
!isDisabled && this.handleActionMouseDown(e, action)}
|
|
1499
1670
|
@mouseup=${(e: MouseEvent) =>
|
|
1500
1671
|
!isDisabled && this.handleActionMouseUp(e, action)}
|
|
1672
|
+
@touchstart=${(e: TouchEvent) =>
|
|
1673
|
+
!isDisabled && this.handleActionTouchStart(e, action)}
|
|
1674
|
+
@touchend=${(e: TouchEvent) =>
|
|
1675
|
+
!isDisabled && this.handleActionTouchEnd(e, action)}
|
|
1501
1676
|
style="cursor: ${isDisabled ? 'not-allowed' : 'pointer'}"
|
|
1502
1677
|
>
|
|
1503
1678
|
${this.renderTitle(config, action, index, isRemoving)}
|
|
@@ -1559,10 +1734,15 @@ export class CanvasNode extends RapidElement {
|
|
|
1559
1734
|
class="body"
|
|
1560
1735
|
@mousedown=${(e: MouseEvent) => this.handleNodeMouseDown(e)}
|
|
1561
1736
|
@mouseup=${(e: MouseEvent) => this.handleNodeMouseUp(e)}
|
|
1737
|
+
@touchstart=${(e: TouchEvent) => this.handleNodeTouchStart(e)}
|
|
1738
|
+
@touchend=${(e: TouchEvent) => this.handleNodeTouchEnd(e)}
|
|
1562
1739
|
style="cursor: pointer;"
|
|
1563
1740
|
>
|
|
1564
|
-
|
|
1565
|
-
|
|
1741
|
+
${renderClamped(
|
|
1742
|
+
html`Save as
|
|
1743
|
+
<span class="result-name">${router.result_name}</span>`,
|
|
1744
|
+
`Save as ${router.result_name}`
|
|
1745
|
+
)}
|
|
1566
1746
|
</div>`
|
|
1567
1747
|
: null}
|
|
1568
1748
|
</div>`;
|
|
@@ -1622,9 +1802,11 @@ export class CanvasNode extends RapidElement {
|
|
|
1622
1802
|
})}
|
|
1623
1803
|
@mousedown=${(e: MouseEvent) => this.handleNodeMouseDown(e)}
|
|
1624
1804
|
@mouseup=${(e: MouseEvent) => this.handleNodeMouseUp(e)}
|
|
1805
|
+
@touchstart=${(e: TouchEvent) => this.handleNodeTouchStart(e)}
|
|
1806
|
+
@touchend=${(e: TouchEvent) => this.handleNodeTouchEnd(e)}
|
|
1625
1807
|
style="cursor: pointer;"
|
|
1626
1808
|
>
|
|
1627
|
-
<div class="cn-title">${displayName}</div>
|
|
1809
|
+
<div class="cn-title" title="${displayName}">${displayName}</div>
|
|
1628
1810
|
${this.renderExit(exit)}
|
|
1629
1811
|
</div>`;
|
|
1630
1812
|
}
|
|
@@ -1697,6 +1879,8 @@ export class CanvasNode extends RapidElement {
|
|
|
1697
1879
|
<div
|
|
1698
1880
|
@mousedown=${(e: MouseEvent) => this.handleNodeMouseDown(e)}
|
|
1699
1881
|
@mouseup=${(e: MouseEvent) => this.handleNodeMouseUp(e)}
|
|
1882
|
+
@touchstart=${(e: TouchEvent) => this.handleNodeTouchStart(e)}
|
|
1883
|
+
@touchend=${(e: TouchEvent) => this.handleNodeTouchEnd(e)}
|
|
1700
1884
|
style="cursor: pointer;"
|
|
1701
1885
|
>
|
|
1702
1886
|
${this.renderNodeTitle(
|