@nyaruka/temba-components 0.142.2 → 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 +8 -0
- package/dist/temba-components.js +266 -192
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/flow/CanvasMenu.js +8 -3
- package/out-tsc/src/flow/CanvasMenu.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +158 -9
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +473 -17
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +8 -8
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/Plumber.js +89 -27
- 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/send_msg.js +11 -8
- package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_subflow.js +3 -1
- package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
- package/out-tsc/src/flow/utils.js +1 -3
- package/out-tsc/src/flow/utils.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 +5 -1
- package/out-tsc/src/simulator/Simulator.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/actions/send_msg/render/long-quick-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/src/flow/CanvasMenu.ts +12 -4
- package/src/flow/CanvasNode.ts +185 -9
- package/src/flow/Editor.ts +552 -19
- package/src/flow/NodeEditor.ts +12 -12
- package/src/flow/Plumber.ts +101 -36
- package/src/flow/StickyNote.ts +76 -4
- package/src/flow/actions/send_msg.ts +11 -8
- package/src/flow/nodes/split_by_subflow.ts +3 -1
- package/src/flow/utils.ts +1 -3
- package/src/list/SortableList.ts +117 -47
- package/src/simulator/Simulator.ts +5 -1
|
@@ -69,15 +69,19 @@ export class CanvasMenu extends RapidElement {
|
|
|
69
69
|
}
|
|
70
70
|
firstUpdated(_changedProperties) {
|
|
71
71
|
super.firstUpdated(_changedProperties);
|
|
72
|
-
// Close menu when clicking outside — use mousedown instead of
|
|
73
|
-
// to avoid being triggered by the click synthesized from a
|
|
74
|
-
// (mousedown on exit + mouseup on canvas = click on
|
|
72
|
+
// Close menu when clicking/tapping outside — use mousedown instead of
|
|
73
|
+
// click to avoid being triggered by the click synthesized from a
|
|
74
|
+
// drag-and-drop (mousedown on exit + mouseup on canvas = click on
|
|
75
|
+
// common ancestor). Also listen for touchstart for touch devices.
|
|
75
76
|
const handleClickOutside = (e) => {
|
|
76
77
|
if (this.open && !this.contains(e.target)) {
|
|
77
78
|
this.close();
|
|
78
79
|
}
|
|
79
80
|
};
|
|
80
81
|
document.addEventListener('mousedown', handleClickOutside);
|
|
82
|
+
document.addEventListener('touchstart', handleClickOutside, {
|
|
83
|
+
passive: true
|
|
84
|
+
});
|
|
81
85
|
// Store cleanup function
|
|
82
86
|
this._clickOutsideHandler = handleClickOutside;
|
|
83
87
|
}
|
|
@@ -85,6 +89,7 @@ export class CanvasMenu extends RapidElement {
|
|
|
85
89
|
super.disconnectedCallback();
|
|
86
90
|
if (this._clickOutsideHandler) {
|
|
87
91
|
document.removeEventListener('mousedown', this._clickOutsideHandler);
|
|
92
|
+
document.removeEventListener('touchstart', this._clickOutsideHandler);
|
|
88
93
|
}
|
|
89
94
|
}
|
|
90
95
|
show(x, y, clickPosition, showStickyNote = true, showReflow = false, showWaitForResponse = true) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CanvasMenu.js","sourceRoot":"","sources":["../../../src/flow/CanvasMenu.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { RapidElement } from '../RapidElement';\nimport { CustomEventType } from '../interfaces';\n\n/**\n * Event detail for canvas menu selection\n */\nexport interface CanvasMenuSelection {\n action:\n | 'sticky'\n | 'action'\n | 'split'\n | 'send_msg'\n | 'wait_for_response'\n | 'reflow';\n position: { x: number; y: number };\n}\n\n/**\n * CanvasMenu - A popup menu for adding items to the flow canvas\n * Displayed when double-clicking on empty canvas space\n */\nexport class CanvasMenu extends RapidElement {\n static get styles() {\n return css`\n :host {\n position: fixed;\n z-index: 10000;\n display: block;\n pointer-events: none;\n }\n\n .menu {\n position: fixed;\n background: white;\n border-radius: var(--curvature);\n box-shadow: var(--dropdown-shadow);\n padding: 0.5em 0;\n width: 265px;\n pointer-events: auto;\n }\n\n .menu-item {\n padding: 0.75em 1.5em;\n cursor: pointer;\n display: flex;\n align-items: flex-start;\n gap: 0.75em;\n transition: background-color 0.15s ease;\n }\n\n .menu-item:hover {\n background-color: rgba(0, 0, 0, 0.05);\n }\n\n .menu-item temba-icon {\n --icon-color: var(--color-text);\n }\n\n .menu-item-title {\n font-weight: 500;\n font-size: 1rem;\n color: var(--color-text-dark);\n }\n\n .divider {\n height: 1px;\n background: rgba(0, 0, 0, 0.1);\n margin: 0.5em 0;\n }\n `;\n }\n\n @property({ type: Number })\n public x = 0;\n\n @property({ type: Number })\n public y = 0;\n\n @property({ type: Boolean })\n public open = false;\n\n @property({ type: Boolean })\n public showStickyNote = true;\n\n @property({ type: Boolean })\n public showWaitForResponse = true;\n\n @property({ type: Boolean })\n public showReflow = false;\n\n @state()\n private clickPosition = { x: 0, y: 0 };\n\n protected firstUpdated(\n _changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.firstUpdated(_changedProperties);\n\n // Close menu when clicking outside — use mousedown instead of click\n // to avoid being triggered by the click synthesized from a drag-and-drop\n // (mousedown on exit + mouseup on canvas = click on common ancestor)\n const handleClickOutside = (e: MouseEvent) => {\n if (this.open && !this.contains(e.target as Node)) {\n this.close();\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n\n // Store cleanup function\n (this as any)._clickOutsideHandler = handleClickOutside;\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n if ((this as any)._clickOutsideHandler) {\n document.removeEventListener(\n 'mousedown',\n (this as any)._clickOutsideHandler\n );\n }\n }\n\n public show(\n x: number,\n y: number,\n clickPosition: { x: number; y: number },\n showStickyNote: boolean = true,\n showReflow: boolean = false,\n showWaitForResponse: boolean = true\n ) {\n this.x = x;\n this.y = y;\n this.clickPosition = clickPosition;\n this.showStickyNote = showStickyNote;\n this.showReflow = showReflow;\n this.showWaitForResponse = showWaitForResponse;\n this.open = true;\n\n // Adjust position after menu renders to ensure it fits on screen\n requestAnimationFrame(() => {\n this.adjustPosition();\n });\n }\n\n private adjustPosition(): void {\n const menuElement = this.shadowRoot?.querySelector('.menu') as HTMLElement;\n if (!menuElement) return;\n\n const menuRect = menuElement.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const margin = 10; // margin from viewport edges\n\n let adjustedX = this.x;\n let adjustedY = this.y;\n\n // Check if menu goes off the right edge\n if (this.x + menuRect.width + margin > viewportWidth) {\n adjustedX = viewportWidth - menuRect.width - margin;\n }\n\n // Check if menu goes off the bottom edge\n if (this.y + menuRect.height + margin > viewportHeight) {\n adjustedY = viewportHeight - menuRect.height - margin;\n }\n\n // Update position if needed\n if (adjustedX !== this.x || adjustedY !== this.y) {\n this.x = adjustedX;\n this.y = adjustedY;\n }\n }\n\n public close(fireCanceledEvent: boolean = true) {\n if (this.open) {\n this.open = false;\n // Fire close event so parent can clean up, but only if not from a selection\n if (fireCanceledEvent) {\n this.fireCustomEvent(CustomEventType.Canceled, {});\n }\n }\n }\n\n private handleMenuItemClick(action: CanvasMenuSelection['action']) {\n this.fireCustomEvent(CustomEventType.Selection, {\n action,\n position: this.clickPosition\n } as CanvasMenuSelection);\n // Close without firing canceled event since we made a selection\n this.close(false);\n }\n\n public render(): TemplateResult {\n if (!this.open) {\n return html``;\n }\n\n return html`\n <div class=\"menu\" style=\"left: ${this.x}px; top: ${this.y}px;\">\n <div\n class=\"menu-item\"\n @click=${() => this.handleMenuItemClick('send_msg')}\n >\n <temba-icon name=\"send\" size=\"1.25\"></temba-icon>\n <div class=\"menu-item-title\">Send Message</div>\n </div>\n\n ${this.showWaitForResponse\n ? html`\n <div\n class=\"menu-item\"\n @click=${() => this.handleMenuItemClick('wait_for_response')}\n >\n <temba-icon name=\"message\" size=\"1.25\"></temba-icon>\n <div class=\"menu-item-title\">Wait for Response</div>\n </div>\n `\n : ''}\n\n <div\n class=\"menu-item\"\n @click=${() => this.handleMenuItemClick('action')}\n >\n <temba-icon name=\"action\" size=\"1.25\"></temba-icon>\n <div class=\"menu-item-title\">Add Action</div>\n </div>\n\n <div\n class=\"menu-item\"\n @click=${() => this.handleMenuItemClick('split')}\n >\n <temba-icon name=\"split\" size=\"1.25\"></temba-icon>\n <div class=\"menu-item-title\">Add Split</div>\n </div>\n\n ${this.showStickyNote\n ? html`\n <div class=\"divider\"></div>\n\n <div\n class=\"menu-item\"\n @click=${() => this.handleMenuItemClick('sticky')}\n >\n <temba-icon name=\"note\" size=\"1.25\"></temba-icon>\n <div class=\"menu-item-title\">Add Sticky Note</div>\n </div>\n `\n : ''}\n ${this.showReflow\n ? html`\n <div class=\"divider\"></div>\n\n <div\n class=\"menu-item\"\n @click=${() => this.handleMenuItemClick('reflow')}\n >\n <temba-icon name=\"flow\" size=\"1.25\"></temba-icon>\n <div class=\"menu-item-title\">Reflow</div>\n </div>\n `\n : ''}\n </div>\n `;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"CanvasMenu.js","sourceRoot":"","sources":["../../../src/flow/CanvasMenu.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { RapidElement } from '../RapidElement';\nimport { CustomEventType } from '../interfaces';\n\n/**\n * Event detail for canvas menu selection\n */\nexport interface CanvasMenuSelection {\n action:\n | 'sticky'\n | 'action'\n | 'split'\n | 'send_msg'\n | 'wait_for_response'\n | 'reflow';\n position: { x: number; y: number };\n}\n\n/**\n * CanvasMenu - A popup menu for adding items to the flow canvas\n * Displayed when double-clicking on empty canvas space\n */\nexport class CanvasMenu extends RapidElement {\n static get styles() {\n return css`\n :host {\n position: fixed;\n z-index: 10000;\n display: block;\n pointer-events: none;\n }\n\n .menu {\n position: fixed;\n background: white;\n border-radius: var(--curvature);\n box-shadow: var(--dropdown-shadow);\n padding: 0.5em 0;\n width: 265px;\n pointer-events: auto;\n }\n\n .menu-item {\n padding: 0.75em 1.5em;\n cursor: pointer;\n display: flex;\n align-items: flex-start;\n gap: 0.75em;\n transition: background-color 0.15s ease;\n }\n\n .menu-item:hover {\n background-color: rgba(0, 0, 0, 0.05);\n }\n\n .menu-item temba-icon {\n --icon-color: var(--color-text);\n }\n\n .menu-item-title {\n font-weight: 500;\n font-size: 1rem;\n color: var(--color-text-dark);\n }\n\n .divider {\n height: 1px;\n background: rgba(0, 0, 0, 0.1);\n margin: 0.5em 0;\n }\n `;\n }\n\n @property({ type: Number })\n public x = 0;\n\n @property({ type: Number })\n public y = 0;\n\n @property({ type: Boolean })\n public open = false;\n\n @property({ type: Boolean })\n public showStickyNote = true;\n\n @property({ type: Boolean })\n public showWaitForResponse = true;\n\n @property({ type: Boolean })\n public showReflow = false;\n\n @state()\n private clickPosition = { x: 0, y: 0 };\n\n protected firstUpdated(\n _changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.firstUpdated(_changedProperties);\n\n // Close menu when clicking/tapping outside — use mousedown instead of\n // click to avoid being triggered by the click synthesized from a\n // drag-and-drop (mousedown on exit + mouseup on canvas = click on\n // common ancestor). Also listen for touchstart for touch devices.\n const handleClickOutside = (e: MouseEvent | TouchEvent) => {\n if (this.open && !this.contains(e.target as Node)) {\n this.close();\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n document.addEventListener('touchstart', handleClickOutside, {\n passive: true\n });\n\n // Store cleanup function\n (this as any)._clickOutsideHandler = handleClickOutside;\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n if ((this as any)._clickOutsideHandler) {\n document.removeEventListener(\n 'mousedown',\n (this as any)._clickOutsideHandler\n );\n document.removeEventListener(\n 'touchstart',\n (this as any)._clickOutsideHandler\n );\n }\n }\n\n public show(\n x: number,\n y: number,\n clickPosition: { x: number; y: number },\n showStickyNote: boolean = true,\n showReflow: boolean = false,\n showWaitForResponse: boolean = true\n ) {\n this.x = x;\n this.y = y;\n this.clickPosition = clickPosition;\n this.showStickyNote = showStickyNote;\n this.showReflow = showReflow;\n this.showWaitForResponse = showWaitForResponse;\n this.open = true;\n\n // Adjust position after menu renders to ensure it fits on screen\n requestAnimationFrame(() => {\n this.adjustPosition();\n });\n }\n\n private adjustPosition(): void {\n const menuElement = this.shadowRoot?.querySelector('.menu') as HTMLElement;\n if (!menuElement) return;\n\n const menuRect = menuElement.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const margin = 10; // margin from viewport edges\n\n let adjustedX = this.x;\n let adjustedY = this.y;\n\n // Check if menu goes off the right edge\n if (this.x + menuRect.width + margin > viewportWidth) {\n adjustedX = viewportWidth - menuRect.width - margin;\n }\n\n // Check if menu goes off the bottom edge\n if (this.y + menuRect.height + margin > viewportHeight) {\n adjustedY = viewportHeight - menuRect.height - margin;\n }\n\n // Update position if needed\n if (adjustedX !== this.x || adjustedY !== this.y) {\n this.x = adjustedX;\n this.y = adjustedY;\n }\n }\n\n public close(fireCanceledEvent: boolean = true) {\n if (this.open) {\n this.open = false;\n // Fire close event so parent can clean up, but only if not from a selection\n if (fireCanceledEvent) {\n this.fireCustomEvent(CustomEventType.Canceled, {});\n }\n }\n }\n\n private handleMenuItemClick(action: CanvasMenuSelection['action']) {\n this.fireCustomEvent(CustomEventType.Selection, {\n action,\n position: this.clickPosition\n } as CanvasMenuSelection);\n // Close without firing canceled event since we made a selection\n this.close(false);\n }\n\n public render(): TemplateResult {\n if (!this.open) {\n return html``;\n }\n\n return html`\n <div class=\"menu\" style=\"left: ${this.x}px; top: ${this.y}px;\">\n <div\n class=\"menu-item\"\n @click=${() => this.handleMenuItemClick('send_msg')}\n >\n <temba-icon name=\"send\" size=\"1.25\"></temba-icon>\n <div class=\"menu-item-title\">Send Message</div>\n </div>\n\n ${this.showWaitForResponse\n ? html`\n <div\n class=\"menu-item\"\n @click=${() => this.handleMenuItemClick('wait_for_response')}\n >\n <temba-icon name=\"message\" size=\"1.25\"></temba-icon>\n <div class=\"menu-item-title\">Wait for Response</div>\n </div>\n `\n : ''}\n\n <div\n class=\"menu-item\"\n @click=${() => this.handleMenuItemClick('action')}\n >\n <temba-icon name=\"action\" size=\"1.25\"></temba-icon>\n <div class=\"menu-item-title\">Add Action</div>\n </div>\n\n <div\n class=\"menu-item\"\n @click=${() => this.handleMenuItemClick('split')}\n >\n <temba-icon name=\"split\" size=\"1.25\"></temba-icon>\n <div class=\"menu-item-title\">Add Split</div>\n </div>\n\n ${this.showStickyNote\n ? html`\n <div class=\"divider\"></div>\n\n <div\n class=\"menu-item\"\n @click=${() => this.handleMenuItemClick('sticky')}\n >\n <temba-icon name=\"note\" size=\"1.25\"></temba-icon>\n <div class=\"menu-item-title\">Add Sticky Note</div>\n </div>\n `\n : ''}\n ${this.showReflow\n ? html`\n <div class=\"divider\"></div>\n\n <div\n class=\"menu-item\"\n @click=${() => this.handleMenuItemClick('reflow')}\n >\n <temba-icon name=\"flow\" size=\"1.25\"></temba-icon>\n <div class=\"menu-item-title\">Reflow</div>\n </div>\n `\n : ''}\n </div>\n `;\n }\n}\n"]}
|
|
@@ -171,24 +171,20 @@ export class CanvasNode extends RapidElement {
|
|
|
171
171
|
}
|
|
172
172
|
.title-spacer {
|
|
173
173
|
width: 1.8em;
|
|
174
|
-
|
|
175
174
|
}
|
|
176
175
|
|
|
177
176
|
.action:hover .drag-handle {
|
|
178
177
|
visibility: visible;
|
|
179
178
|
opacity: 0.7;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
strong {
|
|
185
|
-
font-weight: 500;
|
|
186
179
|
}
|
|
187
180
|
|
|
188
181
|
.action .drag-handle:hover {
|
|
189
182
|
visibility: visible;
|
|
190
183
|
opacity: 1;
|
|
191
|
-
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
strong {
|
|
187
|
+
font-weight: 500;
|
|
192
188
|
}
|
|
193
189
|
|
|
194
190
|
.action .cn-title,
|
|
@@ -215,6 +211,8 @@ export class CanvasNode extends RapidElement {
|
|
|
215
211
|
|
|
216
212
|
.quick-replies {
|
|
217
213
|
margin-top: 0.5em;
|
|
214
|
+
display: flex;
|
|
215
|
+
flex-wrap: wrap;
|
|
218
216
|
}
|
|
219
217
|
|
|
220
218
|
.quick-reply {
|
|
@@ -222,9 +220,14 @@ export class CanvasNode extends RapidElement {
|
|
|
222
220
|
border: 1px solid #e0e0e0;
|
|
223
221
|
border-radius: calc(var(--curvature) * 1.5);
|
|
224
222
|
padding: 0.2em 1em;
|
|
225
|
-
display: inline-block;
|
|
226
223
|
font-size: 0.8em;
|
|
227
224
|
margin: 0.2em;
|
|
225
|
+
flex: 0 1 auto;
|
|
226
|
+
min-width: 0;
|
|
227
|
+
max-width: 100%;
|
|
228
|
+
overflow: hidden;
|
|
229
|
+
text-overflow: ellipsis;
|
|
230
|
+
white-space: nowrap;
|
|
228
231
|
}
|
|
229
232
|
|
|
230
233
|
.router-section {
|
|
@@ -409,6 +412,22 @@ export class CanvasNode extends RapidElement {
|
|
|
409
412
|
color: #9ca3af;
|
|
410
413
|
font-size: 0.9em;
|
|
411
414
|
}
|
|
415
|
+
|
|
416
|
+
/* On touch devices, always show interactive controls.
|
|
417
|
+
The .touch-device class is added to the editor on first touch. */
|
|
418
|
+
.touch-device .remove-button {
|
|
419
|
+
visibility: visible !important;
|
|
420
|
+
opacity: 0.7;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.touch-device .action .drag-handle {
|
|
424
|
+
visibility: visible !important;
|
|
425
|
+
opacity: 0.7;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.touch-device .add-action-button {
|
|
429
|
+
opacity: 0.8 !important;
|
|
430
|
+
}
|
|
412
431
|
}`;
|
|
413
432
|
}
|
|
414
433
|
constructor() {
|
|
@@ -850,6 +869,61 @@ export class CanvasNode extends RapidElement {
|
|
|
850
869
|
this.actionClickStartPos = null;
|
|
851
870
|
this.pendingActionClick = null;
|
|
852
871
|
}
|
|
872
|
+
/* c8 ignore start -- touch-only handlers untestable in headless Chromium */
|
|
873
|
+
handleActionTouchStart(event, action) {
|
|
874
|
+
const target = event.target;
|
|
875
|
+
if (target.closest('.remove-button') ||
|
|
876
|
+
target.closest('.drag-handle') ||
|
|
877
|
+
target.closest('.linked-name') ||
|
|
878
|
+
this.actionRemovingState.has(action.uuid)) {
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
const touch = event.touches[0];
|
|
882
|
+
if (!touch)
|
|
883
|
+
return;
|
|
884
|
+
this.actionClickStartPos = { x: touch.clientX, y: touch.clientY };
|
|
885
|
+
this.pendingActionClick = { action, event: event };
|
|
886
|
+
}
|
|
887
|
+
handleActionTouchEnd(event, action) {
|
|
888
|
+
if (!this.pendingActionClick ||
|
|
889
|
+
this.pendingActionClick.action.uuid !== action.uuid) {
|
|
890
|
+
this.actionClickStartPos = null;
|
|
891
|
+
this.pendingActionClick = null;
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
const target = event.target;
|
|
895
|
+
if (target.closest('.remove-button') ||
|
|
896
|
+
target.closest('.drag-handle') ||
|
|
897
|
+
target.closest('.linked-name') ||
|
|
898
|
+
this.actionRemovingState.has(action.uuid)) {
|
|
899
|
+
this.actionClickStartPos = null;
|
|
900
|
+
this.pendingActionClick = null;
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
const touch = event.changedTouches[0];
|
|
904
|
+
if (this.actionClickStartPos && touch) {
|
|
905
|
+
const deltaX = touch.clientX - this.actionClickStartPos.x;
|
|
906
|
+
const deltaY = touch.clientY - this.actionClickStartPos.y;
|
|
907
|
+
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
908
|
+
const editor = this.closest('temba-flow-editor');
|
|
909
|
+
const editorWasDragging = editor === null || editor === void 0 ? void 0 : editor.dragging;
|
|
910
|
+
if (distance <= DRAG_THRESHOLD && (!editor || !editorWasDragging)) {
|
|
911
|
+
const actionEl = event.currentTarget;
|
|
912
|
+
const origin = actionEl
|
|
913
|
+
? this.getTopCenter(actionEl)
|
|
914
|
+
: { x: touch.clientX, y: touch.clientY };
|
|
915
|
+
this.fireCustomEvent(CustomEventType.ActionEditRequested, {
|
|
916
|
+
action,
|
|
917
|
+
nodeUuid: this.node.uuid,
|
|
918
|
+
originX: origin.x,
|
|
919
|
+
originY: origin.y
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
this.actionClickStartPos = null;
|
|
924
|
+
this.pendingActionClick = null;
|
|
925
|
+
}
|
|
926
|
+
/* c8 ignore stop */
|
|
853
927
|
handleActionClick(event, action) {
|
|
854
928
|
// This method is kept for backward compatibility but should not be used
|
|
855
929
|
// The new mousedown/mouseup approach handles click vs drag properly
|
|
@@ -972,6 +1046,73 @@ export class CanvasNode extends RapidElement {
|
|
|
972
1046
|
this.nodeClickStartPos = null;
|
|
973
1047
|
this.pendingNodeClick = null;
|
|
974
1048
|
}
|
|
1049
|
+
/* c8 ignore start -- touch-only handlers */
|
|
1050
|
+
handleNodeTouchStart(event) {
|
|
1051
|
+
const target = event.target;
|
|
1052
|
+
if (target.closest('.remove-button') ||
|
|
1053
|
+
target.closest('.exit') ||
|
|
1054
|
+
target.closest('.exit-wrapper') ||
|
|
1055
|
+
target.closest('.drag-handle') ||
|
|
1056
|
+
target.closest('.linked-name') ||
|
|
1057
|
+
this.actionRemovingState.has(this.node.uuid)) {
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
const touch = event.touches[0];
|
|
1061
|
+
if (!touch)
|
|
1062
|
+
return;
|
|
1063
|
+
this.nodeClickStartPos = { x: touch.clientX, y: touch.clientY };
|
|
1064
|
+
this.pendingNodeClick = { event: event };
|
|
1065
|
+
}
|
|
1066
|
+
handleNodeTouchEnd(event) {
|
|
1067
|
+
if (!this.pendingNodeClick) {
|
|
1068
|
+
this.nodeClickStartPos = null;
|
|
1069
|
+
this.pendingNodeClick = null;
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
const target = event.target;
|
|
1073
|
+
if (target.closest('.remove-button') ||
|
|
1074
|
+
target.closest('.exit') ||
|
|
1075
|
+
target.closest('.exit-wrapper') ||
|
|
1076
|
+
target.closest('.drag-handle') ||
|
|
1077
|
+
target.closest('.linked-name') ||
|
|
1078
|
+
this.actionRemovingState.has(this.node.uuid)) {
|
|
1079
|
+
this.nodeClickStartPos = null;
|
|
1080
|
+
this.pendingNodeClick = null;
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
const touch = event.changedTouches[0];
|
|
1084
|
+
if (this.nodeClickStartPos && touch) {
|
|
1085
|
+
const deltaX = touch.clientX - this.nodeClickStartPos.x;
|
|
1086
|
+
const deltaY = touch.clientY - this.nodeClickStartPos.y;
|
|
1087
|
+
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
1088
|
+
const editor = this.closest('temba-flow-editor');
|
|
1089
|
+
const editorWasDragging = editor === null || editor === void 0 ? void 0 : editor.dragging;
|
|
1090
|
+
if (distance <= 5 && (!editor || !editorWasDragging)) {
|
|
1091
|
+
if (this.node.router) {
|
|
1092
|
+
const origin = this.getTopCenter(this);
|
|
1093
|
+
if (this.node.actions && this.node.actions.length === 1) {
|
|
1094
|
+
this.fireCustomEvent(CustomEventType.ActionEditRequested, {
|
|
1095
|
+
action: this.node.actions[0],
|
|
1096
|
+
nodeUuid: this.node.uuid,
|
|
1097
|
+
originX: origin.x,
|
|
1098
|
+
originY: origin.y
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
else {
|
|
1102
|
+
this.fireCustomEvent(CustomEventType.NodeEditRequested, {
|
|
1103
|
+
node: this.node,
|
|
1104
|
+
nodeUI: this.ui,
|
|
1105
|
+
originX: origin.x,
|
|
1106
|
+
originY: origin.y
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
this.nodeClickStartPos = null;
|
|
1113
|
+
this.pendingNodeClick = null;
|
|
1114
|
+
}
|
|
1115
|
+
/* c8 ignore stop */
|
|
975
1116
|
handleAddActionClick(event) {
|
|
976
1117
|
event.preventDefault();
|
|
977
1118
|
event.stopPropagation();
|
|
@@ -1230,6 +1371,8 @@ export class CanvasNode extends RapidElement {
|
|
|
1230
1371
|
class="action-content ${hasIssues ? 'has-issues' : ''}"
|
|
1231
1372
|
@mousedown=${(e) => !isDisabled && this.handleActionMouseDown(e, action)}
|
|
1232
1373
|
@mouseup=${(e) => !isDisabled && this.handleActionMouseUp(e, action)}
|
|
1374
|
+
@touchstart=${(e) => !isDisabled && this.handleActionTouchStart(e, action)}
|
|
1375
|
+
@touchend=${(e) => !isDisabled && this.handleActionTouchEnd(e, action)}
|
|
1233
1376
|
style="cursor: ${isDisabled ? 'not-allowed' : 'pointer'}"
|
|
1234
1377
|
>
|
|
1235
1378
|
${this.renderTitle(config, action, index, isRemoving)}
|
|
@@ -1283,6 +1426,8 @@ export class CanvasNode extends RapidElement {
|
|
|
1283
1426
|
class="body"
|
|
1284
1427
|
@mousedown=${(e) => this.handleNodeMouseDown(e)}
|
|
1285
1428
|
@mouseup=${(e) => this.handleNodeMouseUp(e)}
|
|
1429
|
+
@touchstart=${(e) => this.handleNodeTouchStart(e)}
|
|
1430
|
+
@touchend=${(e) => this.handleNodeTouchEnd(e)}
|
|
1286
1431
|
style="cursor: pointer;"
|
|
1287
1432
|
>
|
|
1288
1433
|
${renderClamped(html `Save as
|
|
@@ -1332,6 +1477,8 @@ export class CanvasNode extends RapidElement {
|
|
|
1332
1477
|
})}
|
|
1333
1478
|
@mousedown=${(e) => this.handleNodeMouseDown(e)}
|
|
1334
1479
|
@mouseup=${(e) => this.handleNodeMouseUp(e)}
|
|
1480
|
+
@touchstart=${(e) => this.handleNodeTouchStart(e)}
|
|
1481
|
+
@touchend=${(e) => this.handleNodeTouchEnd(e)}
|
|
1335
1482
|
style="cursor: pointer;"
|
|
1336
1483
|
>
|
|
1337
1484
|
<div class="cn-title" title="${displayName}">${displayName}</div>
|
|
@@ -1396,6 +1543,8 @@ export class CanvasNode extends RapidElement {
|
|
|
1396
1543
|
<div
|
|
1397
1544
|
@mousedown=${(e) => this.handleNodeMouseDown(e)}
|
|
1398
1545
|
@mouseup=${(e) => this.handleNodeMouseUp(e)}
|
|
1546
|
+
@touchstart=${(e) => this.handleNodeTouchStart(e)}
|
|
1547
|
+
@touchend=${(e) => this.handleNodeTouchEnd(e)}
|
|
1399
1548
|
style="cursor: pointer;"
|
|
1400
1549
|
>
|
|
1401
1550
|
${this.renderNodeTitle(nodeConfig, this.node, this.ui, this.actionRemovingState.has(this.node.uuid))}
|