@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
package/src/flow/NodeEditor.ts
CHANGED
|
@@ -388,9 +388,7 @@ export class NodeEditor extends RapidElement {
|
|
|
388
388
|
|
|
389
389
|
.accordion-toggle-icon {
|
|
390
390
|
color: #999;
|
|
391
|
-
transition:
|
|
392
|
-
transform 0.2s ease,
|
|
393
|
-
opacity 0.3s ease;
|
|
391
|
+
transition: transform 0.2s ease, opacity 0.3s ease;
|
|
394
392
|
}
|
|
395
393
|
|
|
396
394
|
.accordion-toggle-icon.expanded {
|
|
@@ -2194,18 +2192,20 @@ export class NodeEditor extends RapidElement {
|
|
|
2194
2192
|
class="accordion-checkmark-icon"
|
|
2195
2193
|
></temba-icon>`
|
|
2196
2194
|
: showBubble
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2195
|
+
? html`<div
|
|
2196
|
+
class="accordion-count-bubble ${!showBubble
|
|
2197
|
+
? 'hidden'
|
|
2198
|
+
: ''}"
|
|
2199
|
+
>
|
|
2200
|
+
${valueCount}
|
|
2201
|
+
</div>`
|
|
2202
|
+
: ''}
|
|
2205
2203
|
</div>`}
|
|
2206
2204
|
</div>
|
|
2207
2205
|
<div
|
|
2208
|
-
class="accordion-content ${isCollapsed
|
|
2206
|
+
class="accordion-content ${isCollapsed
|
|
2207
|
+
? 'collapsed'
|
|
2208
|
+
: 'expanded'}"
|
|
2209
2209
|
>
|
|
2210
2210
|
${section.items.map((item) =>
|
|
2211
2211
|
this.renderLayoutItem(item, config, renderedFields)
|
package/src/flow/Plumber.ts
CHANGED
|
@@ -2,6 +2,21 @@ import { isRightClick } from './utils';
|
|
|
2
2
|
|
|
3
3
|
export type TargetFace = 'top' | 'left' | 'right';
|
|
4
4
|
|
|
5
|
+
/** Extract clientX/clientY from a MouseEvent or the first touch of a TouchEvent. */
|
|
6
|
+
function getClientCoords(e: MouseEvent | TouchEvent): {
|
|
7
|
+
clientX: number;
|
|
8
|
+
clientY: number;
|
|
9
|
+
} {
|
|
10
|
+
if ('touches' in e) {
|
|
11
|
+
const touch = e.touches[0] || (e as TouchEvent).changedTouches[0];
|
|
12
|
+
return { clientX: touch.clientX, clientY: touch.clientY };
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
clientX: (e as MouseEvent).clientX,
|
|
16
|
+
clientY: (e as MouseEvent).clientY
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
5
20
|
// Shared arrow/drag constants used by both Plumber and Editor
|
|
6
21
|
export const ARROW_LENGTH = 13;
|
|
7
22
|
export const ARROW_HALF_WIDTH = 6.5;
|
|
@@ -33,8 +48,8 @@ interface DragState {
|
|
|
33
48
|
svgEl: SVGSVGElement;
|
|
34
49
|
pathEl: SVGPathElement;
|
|
35
50
|
arrowEl: SVGPolygonElement;
|
|
36
|
-
onMove: (e: MouseEvent) => void;
|
|
37
|
-
onUp: (e: MouseEvent) => void;
|
|
51
|
+
onMove: (e: MouseEvent | TouchEvent) => void;
|
|
52
|
+
onUp: (e: MouseEvent | TouchEvent) => void;
|
|
38
53
|
}
|
|
39
54
|
|
|
40
55
|
/**
|
|
@@ -242,56 +257,75 @@ export class Plumber {
|
|
|
242
257
|
let pendingDrag: {
|
|
243
258
|
startX: number;
|
|
244
259
|
startY: number;
|
|
245
|
-
onMove: (e: MouseEvent) => void;
|
|
246
|
-
onUp: (e: MouseEvent) => void;
|
|
260
|
+
onMove: (e: MouseEvent | TouchEvent) => void;
|
|
261
|
+
onUp: (e: MouseEvent | TouchEvent) => void;
|
|
262
|
+
isTouch: boolean;
|
|
247
263
|
} | null = null;
|
|
248
264
|
|
|
249
265
|
const DRAG_THRESHOLD = 5;
|
|
250
266
|
|
|
251
|
-
const
|
|
252
|
-
if (isRightClick(e)) return;
|
|
267
|
+
const beginPendingDrag = (e: MouseEvent | TouchEvent) => {
|
|
268
|
+
if ('button' in e && isRightClick(e)) return;
|
|
253
269
|
|
|
254
270
|
// Don't start drag from exit if it already has a connection —
|
|
255
271
|
// existing connections are picked up from the arrowhead instead
|
|
256
272
|
if (this.connections.has(exitId)) return;
|
|
257
273
|
|
|
258
|
-
const
|
|
259
|
-
|
|
274
|
+
const isTouch = 'touches' in e;
|
|
275
|
+
if (isTouch) e.preventDefault();
|
|
276
|
+
|
|
277
|
+
const { clientX: startX, clientY: startY } = getClientCoords(e);
|
|
260
278
|
|
|
261
279
|
const nodeEl = element.closest('temba-flow-node');
|
|
262
280
|
const scope = nodeEl?.getAttribute('uuid') || '';
|
|
263
281
|
const originalTargetId: string | null = null;
|
|
264
282
|
|
|
265
|
-
const onMove = (me: MouseEvent) => {
|
|
266
|
-
const
|
|
267
|
-
const
|
|
283
|
+
const onMove = (me: MouseEvent | TouchEvent) => {
|
|
284
|
+
const { clientX, clientY } = getClientCoords(me);
|
|
285
|
+
const dx = clientX - startX;
|
|
286
|
+
const dy = clientY - startY;
|
|
268
287
|
if (Math.sqrt(dx * dx + dy * dy) > DRAG_THRESHOLD) {
|
|
269
288
|
// Exceeded threshold — start actual drag
|
|
270
|
-
|
|
271
|
-
document.removeEventListener('mouseup', onUp);
|
|
272
|
-
pendingDrag = null;
|
|
289
|
+
removePendingListeners();
|
|
273
290
|
this.startDrag(exitId, scope, originalTargetId, me);
|
|
274
291
|
}
|
|
275
292
|
};
|
|
276
293
|
|
|
277
294
|
const onUp = () => {
|
|
278
|
-
//
|
|
295
|
+
// Released without dragging — let click handler fire
|
|
296
|
+
removePendingListeners();
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const removePendingListeners = () => {
|
|
279
300
|
document.removeEventListener('mousemove', onMove);
|
|
280
301
|
document.removeEventListener('mouseup', onUp);
|
|
302
|
+
document.removeEventListener('touchmove', onMove);
|
|
303
|
+
document.removeEventListener('touchend', onUp);
|
|
304
|
+
document.removeEventListener('touchcancel', onUp);
|
|
281
305
|
pendingDrag = null;
|
|
282
306
|
};
|
|
283
307
|
|
|
284
308
|
document.addEventListener('mousemove', onMove);
|
|
285
309
|
document.addEventListener('mouseup', onUp);
|
|
286
|
-
|
|
310
|
+
document.addEventListener('touchmove', onMove, { passive: false });
|
|
311
|
+
document.addEventListener('touchend', onUp);
|
|
312
|
+
document.addEventListener('touchcancel', onUp);
|
|
313
|
+
pendingDrag = { startX, startY, onMove, onUp, isTouch };
|
|
287
314
|
};
|
|
288
315
|
|
|
289
|
-
element.addEventListener('mousedown',
|
|
316
|
+
element.addEventListener('mousedown', beginPendingDrag);
|
|
317
|
+
element.addEventListener('touchstart', beginPendingDrag, {
|
|
318
|
+
passive: false
|
|
319
|
+
});
|
|
290
320
|
this.sources.set(exitId, () => {
|
|
291
|
-
element.removeEventListener('mousedown',
|
|
321
|
+
element.removeEventListener('mousedown', beginPendingDrag);
|
|
322
|
+
element.removeEventListener('touchstart', beginPendingDrag);
|
|
292
323
|
if (pendingDrag) {
|
|
293
324
|
document.removeEventListener('mousemove', pendingDrag.onMove);
|
|
294
325
|
document.removeEventListener('mouseup', pendingDrag.onUp);
|
|
326
|
+
document.removeEventListener('touchmove', pendingDrag.onMove);
|
|
327
|
+
document.removeEventListener('touchend', pendingDrag.onUp);
|
|
328
|
+
document.removeEventListener('touchcancel', pendingDrag.onUp);
|
|
295
329
|
pendingDrag = null;
|
|
296
330
|
}
|
|
297
331
|
});
|
|
@@ -714,19 +748,23 @@ export class Plumber {
|
|
|
714
748
|
|
|
715
749
|
// Make arrowhead draggable for picking up existing connections
|
|
716
750
|
const DRAG_THRESHOLD = 5;
|
|
717
|
-
const
|
|
718
|
-
if (isRightClick(e)) return;
|
|
751
|
+
const onArrowDown = (e: MouseEvent | TouchEvent) => {
|
|
752
|
+
if ('button' in e && isRightClick(e)) return;
|
|
719
753
|
e.stopPropagation();
|
|
754
|
+
if ('touches' in e) e.preventDefault();
|
|
720
755
|
|
|
721
|
-
const startX = e
|
|
722
|
-
const startY = e.clientY;
|
|
756
|
+
const { clientX: startX, clientY: startY } = getClientCoords(e);
|
|
723
757
|
|
|
724
|
-
const onMove = (me: MouseEvent) => {
|
|
725
|
-
const
|
|
726
|
-
const
|
|
758
|
+
const onMove = (me: MouseEvent | TouchEvent) => {
|
|
759
|
+
const { clientX, clientY } = getClientCoords(me);
|
|
760
|
+
const dx = clientX - startX;
|
|
761
|
+
const dy = clientY - startY;
|
|
727
762
|
if (Math.sqrt(dx * dx + dy * dy) > DRAG_THRESHOLD) {
|
|
728
763
|
document.removeEventListener('mousemove', onMove);
|
|
729
764
|
document.removeEventListener('mouseup', onUp);
|
|
765
|
+
document.removeEventListener('touchmove', onMove);
|
|
766
|
+
document.removeEventListener('touchend', onUp);
|
|
767
|
+
document.removeEventListener('touchcancel', onUp);
|
|
730
768
|
this.startDrag(exitId, scope, toId, me);
|
|
731
769
|
}
|
|
732
770
|
};
|
|
@@ -734,12 +772,21 @@ export class Plumber {
|
|
|
734
772
|
const onUp = () => {
|
|
735
773
|
document.removeEventListener('mousemove', onMove);
|
|
736
774
|
document.removeEventListener('mouseup', onUp);
|
|
775
|
+
document.removeEventListener('touchmove', onMove);
|
|
776
|
+
document.removeEventListener('touchend', onUp);
|
|
777
|
+
document.removeEventListener('touchcancel', onUp);
|
|
737
778
|
};
|
|
738
779
|
|
|
739
780
|
document.addEventListener('mousemove', onMove);
|
|
740
781
|
document.addEventListener('mouseup', onUp);
|
|
782
|
+
document.addEventListener('touchmove', onMove, { passive: false });
|
|
783
|
+
document.addEventListener('touchend', onUp);
|
|
784
|
+
document.addEventListener('touchcancel', onUp);
|
|
741
785
|
};
|
|
742
|
-
arrowEl.addEventListener('mousedown',
|
|
786
|
+
arrowEl.addEventListener('mousedown', onArrowDown);
|
|
787
|
+
arrowEl.addEventListener('touchstart', onArrowDown, { passive: false });
|
|
788
|
+
pathEl.addEventListener('mousedown', onArrowDown);
|
|
789
|
+
pathEl.addEventListener('touchstart', onArrowDown, { passive: false });
|
|
743
790
|
|
|
744
791
|
// Mark the exit element as connected
|
|
745
792
|
const exitEl = document.getElementById(exitId);
|
|
@@ -1202,10 +1249,18 @@ export class Plumber {
|
|
|
1202
1249
|
exitId: string,
|
|
1203
1250
|
scope: string,
|
|
1204
1251
|
originalTargetId: string | null,
|
|
1205
|
-
e: MouseEvent
|
|
1252
|
+
e: MouseEvent | TouchEvent
|
|
1206
1253
|
) {
|
|
1207
|
-
//
|
|
1208
|
-
|
|
1254
|
+
// Hide (don't remove) the existing connection SVG while dragging.
|
|
1255
|
+
// On iOS Safari, removing the element that received the original
|
|
1256
|
+
// touchstart can trigger touchcancel, prematurely ending the drag.
|
|
1257
|
+
// We defer removal until the drag ends.
|
|
1258
|
+
const oldConn = this.connections.get(exitId);
|
|
1259
|
+
if (oldConn) {
|
|
1260
|
+
oldConn.svgEl.style.display = 'none';
|
|
1261
|
+
const overlay = this.overlays.get(exitId);
|
|
1262
|
+
if (overlay) overlay.style.display = 'none';
|
|
1263
|
+
}
|
|
1209
1264
|
|
|
1210
1265
|
const { svgEl, pathEl, arrowEl } = this.createSVGElement();
|
|
1211
1266
|
svgEl.classList.add('dragging');
|
|
@@ -1283,26 +1338,33 @@ export class Plumber {
|
|
|
1283
1338
|
};
|
|
1284
1339
|
|
|
1285
1340
|
// Initial path to cursor (convert viewport to canvas coordinates)
|
|
1286
|
-
const
|
|
1287
|
-
const
|
|
1341
|
+
const { clientX: initX, clientY: initY } = getClientCoords(e);
|
|
1342
|
+
const cursorX = this.toCanvas(initX - canvasRect.left);
|
|
1343
|
+
const cursorY = this.toCanvas(initY - canvasRect.top);
|
|
1288
1344
|
updateDragPath(cursorX, cursorY);
|
|
1289
1345
|
|
|
1290
1346
|
this.connectionDragging = true;
|
|
1291
1347
|
|
|
1292
|
-
const onMove = (me: MouseEvent) => {
|
|
1348
|
+
const onMove = (me: MouseEvent | TouchEvent) => {
|
|
1349
|
+
if ('touches' in me) me.preventDefault();
|
|
1293
1350
|
// Re-read canvasRect each move since scroll may have changed
|
|
1294
1351
|
const rect = this.canvas.getBoundingClientRect();
|
|
1295
|
-
const
|
|
1296
|
-
const
|
|
1352
|
+
const { clientX, clientY } = getClientCoords(me);
|
|
1353
|
+
const cx = this.toCanvas(clientX - rect.left);
|
|
1354
|
+
const cy = this.toCanvas(clientY - rect.top);
|
|
1297
1355
|
updateDragPath(cx, cy);
|
|
1298
1356
|
};
|
|
1299
1357
|
|
|
1300
|
-
const onUp = (_me: MouseEvent) => {
|
|
1358
|
+
const onUp = (_me: MouseEvent | TouchEvent) => {
|
|
1301
1359
|
document.removeEventListener('mousemove', onMove);
|
|
1302
1360
|
document.removeEventListener('mouseup', onUp);
|
|
1361
|
+
document.removeEventListener('touchmove', onMove);
|
|
1362
|
+
document.removeEventListener('touchend', onUp);
|
|
1363
|
+
document.removeEventListener('touchcancel', onUp);
|
|
1303
1364
|
|
|
1304
|
-
// Remove the drag SVG
|
|
1365
|
+
// Remove the drag SVG and the hidden old connection SVG
|
|
1305
1366
|
svgEl.remove();
|
|
1367
|
+
this.removeConnectionSVG(exitId);
|
|
1306
1368
|
this.connectionDragging = false;
|
|
1307
1369
|
this.dragState = null;
|
|
1308
1370
|
|
|
@@ -1317,6 +1379,9 @@ export class Plumber {
|
|
|
1317
1379
|
|
|
1318
1380
|
document.addEventListener('mousemove', onMove);
|
|
1319
1381
|
document.addEventListener('mouseup', onUp);
|
|
1382
|
+
document.addEventListener('touchmove', onMove, { passive: false });
|
|
1383
|
+
document.addEventListener('touchend', onUp);
|
|
1384
|
+
document.addEventListener('touchcancel', onUp);
|
|
1320
1385
|
|
|
1321
1386
|
this.dragState = {
|
|
1322
1387
|
sourceId: exitId,
|
package/src/flow/StickyNote.ts
CHANGED
|
@@ -21,6 +21,11 @@ export class StickyNote extends RapidElement {
|
|
|
21
21
|
@property({ type: Boolean })
|
|
22
22
|
private colorPickerExpanded = false;
|
|
23
23
|
|
|
24
|
+
// On touch devices, contenteditable starts false to prevent Apple Pencil
|
|
25
|
+
// Scribble from hijacking touches. It is set to true on explicit tap.
|
|
26
|
+
private isTouchDevice = navigator.maxTouchPoints > 0;
|
|
27
|
+
private editingField: HTMLElement | null = null;
|
|
28
|
+
|
|
24
29
|
@fromStore(zustand, (state: AppState) => state.isTranslating)
|
|
25
30
|
private isTranslating!: boolean;
|
|
26
31
|
|
|
@@ -289,6 +294,7 @@ export class StickyNote extends RapidElement {
|
|
|
289
294
|
}
|
|
290
295
|
|
|
291
296
|
private handleTitleBlur(event: FocusEvent): void {
|
|
297
|
+
this.handleContentBlurForTouch(event);
|
|
292
298
|
const target = event.target as HTMLElement;
|
|
293
299
|
const newTitle = target.textContent || '';
|
|
294
300
|
|
|
@@ -304,6 +310,7 @@ export class StickyNote extends RapidElement {
|
|
|
304
310
|
}
|
|
305
311
|
|
|
306
312
|
private handleBodyBlur(event: FocusEvent): void {
|
|
313
|
+
this.handleContentBlurForTouch(event);
|
|
307
314
|
const target = event.target as HTMLElement;
|
|
308
315
|
const newBody = target.innerText || '';
|
|
309
316
|
|
|
@@ -318,6 +325,46 @@ export class StickyNote extends RapidElement {
|
|
|
318
325
|
this.requestUpdate();
|
|
319
326
|
}
|
|
320
327
|
|
|
328
|
+
/* c8 ignore start -- touch-only handlers untestable in headless Chromium */
|
|
329
|
+
private handleDragHandleTouchStart(event: TouchEvent): void {
|
|
330
|
+
// Prevent Apple Pencil Scribble from activating on the adjacent
|
|
331
|
+
// contenteditable fields when touching/dragging the handle.
|
|
332
|
+
event.preventDefault();
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* On touch devices, contenteditable is off by default. A tap on the
|
|
337
|
+
* title or body enables it and focuses the element for editing.
|
|
338
|
+
*/
|
|
339
|
+
private handleContentTap(event: TouchEvent): void {
|
|
340
|
+
if (!this.isTouchDevice) return;
|
|
341
|
+
const target = event.target as HTMLElement;
|
|
342
|
+
if (
|
|
343
|
+
!target.classList.contains('sticky-title') &&
|
|
344
|
+
!target.classList.contains('sticky-body')
|
|
345
|
+
)
|
|
346
|
+
return;
|
|
347
|
+
|
|
348
|
+
// Enable editing and focus
|
|
349
|
+
target.setAttribute('contenteditable', 'true');
|
|
350
|
+
this.editingField = target;
|
|
351
|
+
target.focus();
|
|
352
|
+
event.stopPropagation();
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* When a contenteditable field loses focus on a touch device,
|
|
357
|
+
* disable contenteditable again to prevent Scribble.
|
|
358
|
+
*/
|
|
359
|
+
private handleContentBlurForTouch(event: FocusEvent): void {
|
|
360
|
+
const target = event.target as HTMLElement;
|
|
361
|
+
if (this.isTouchDevice && this.editingField === target) {
|
|
362
|
+
target.setAttribute('contenteditable', 'false');
|
|
363
|
+
this.editingField = null;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
/* c8 ignore stop */
|
|
367
|
+
|
|
321
368
|
private handleContentMouseDown(event: MouseEvent): void {
|
|
322
369
|
// If this sticky note is selected, don't stop propagation
|
|
323
370
|
// so that group dragging can work
|
|
@@ -356,11 +403,19 @@ export class StickyNote extends RapidElement {
|
|
|
356
403
|
this.colorPickerExpanded = false;
|
|
357
404
|
}
|
|
358
405
|
|
|
406
|
+
/* c8 ignore next 5 -- touch-only */
|
|
407
|
+
private handleColorPickerTap(event: TouchEvent): void {
|
|
408
|
+
event.stopPropagation();
|
|
409
|
+
event.preventDefault();
|
|
410
|
+
this.colorPickerExpanded = !this.colorPickerExpanded;
|
|
411
|
+
}
|
|
412
|
+
|
|
359
413
|
private handleColorOptionClick(
|
|
360
|
-
event: MouseEvent,
|
|
414
|
+
event: MouseEvent | TouchEvent,
|
|
361
415
|
color: 'yellow' | 'blue' | 'pink' | 'green' | 'gray'
|
|
362
416
|
): void {
|
|
363
417
|
event.stopPropagation();
|
|
418
|
+
event.preventDefault();
|
|
364
419
|
|
|
365
420
|
if (this.data && color !== this.data.color) {
|
|
366
421
|
getStore()
|
|
@@ -391,23 +446,29 @@ export class StickyNote extends RapidElement {
|
|
|
391
446
|
data-uuid="${this.uuid}"
|
|
392
447
|
>
|
|
393
448
|
<div class="sticky-title-container">
|
|
394
|
-
<temba-icon
|
|
449
|
+
<temba-icon
|
|
450
|
+
name="drag"
|
|
451
|
+
class="drag-handle"
|
|
452
|
+
@touchstart=${this.handleDragHandleTouchStart}
|
|
453
|
+
></temba-icon>
|
|
395
454
|
<div
|
|
396
455
|
class="sticky-title"
|
|
397
|
-
contenteditable="${!this.isTranslating}"
|
|
456
|
+
contenteditable="${!this.isTranslating && !this.isTouchDevice}"
|
|
398
457
|
@blur="${this.handleTitleBlur}"
|
|
399
458
|
@keydown="${this.handleTitleKeyDown}"
|
|
400
459
|
@mousedown="${this.handleContentMouseDown}"
|
|
460
|
+
@touchend="${this.handleContentTap}"
|
|
401
461
|
.textContent="${this.data.title}"
|
|
402
462
|
></div>
|
|
403
463
|
</div>
|
|
404
464
|
<div class="sticky-body-container">
|
|
405
465
|
<div
|
|
406
466
|
class="sticky-body"
|
|
407
|
-
contenteditable="${!this.isTranslating}"
|
|
467
|
+
contenteditable="${!this.isTranslating && !this.isTouchDevice}"
|
|
408
468
|
@blur="${this.handleBodyBlur}"
|
|
409
469
|
@keydown="${this.handleBodyKeyDown}"
|
|
410
470
|
@mousedown="${this.handleContentMouseDown}"
|
|
471
|
+
@touchend="${this.handleContentTap}"
|
|
411
472
|
.textContent="${this.data.body}"
|
|
412
473
|
></div>
|
|
413
474
|
${!this.isTranslating
|
|
@@ -419,6 +480,7 @@ export class StickyNote extends RapidElement {
|
|
|
419
480
|
class="color-picker"
|
|
420
481
|
@mouseenter="${this.handleColorPickerMouseEnter}"
|
|
421
482
|
@mouseleave="${this.handleColorPickerMouseLeave}"
|
|
483
|
+
@touchend="${this.handleColorPickerTap}"
|
|
422
484
|
>
|
|
423
485
|
<div
|
|
424
486
|
class="color-options ${this.colorPickerExpanded
|
|
@@ -429,26 +491,36 @@ export class StickyNote extends RapidElement {
|
|
|
429
491
|
class="color-option yellow"
|
|
430
492
|
@click="${(e: MouseEvent) =>
|
|
431
493
|
this.handleColorOptionClick(e, 'yellow')}"
|
|
494
|
+
@touchend="${(e: TouchEvent) =>
|
|
495
|
+
this.handleColorOptionClick(e, 'yellow')}"
|
|
432
496
|
></div>
|
|
433
497
|
<div
|
|
434
498
|
class="color-option blue"
|
|
435
499
|
@click="${(e: MouseEvent) =>
|
|
436
500
|
this.handleColorOptionClick(e, 'blue')}"
|
|
501
|
+
@touchend="${(e: TouchEvent) =>
|
|
502
|
+
this.handleColorOptionClick(e, 'blue')}"
|
|
437
503
|
></div>
|
|
438
504
|
<div
|
|
439
505
|
class="color-option pink"
|
|
440
506
|
@click="${(e: MouseEvent) =>
|
|
441
507
|
this.handleColorOptionClick(e, 'pink')}"
|
|
508
|
+
@touchend="${(e: TouchEvent) =>
|
|
509
|
+
this.handleColorOptionClick(e, 'pink')}"
|
|
442
510
|
></div>
|
|
443
511
|
<div
|
|
444
512
|
class="color-option green"
|
|
445
513
|
@click="${(e: MouseEvent) =>
|
|
446
514
|
this.handleColorOptionClick(e, 'green')}"
|
|
515
|
+
@touchend="${(e: TouchEvent) =>
|
|
516
|
+
this.handleColorOptionClick(e, 'green')}"
|
|
447
517
|
></div>
|
|
448
518
|
<div
|
|
449
519
|
class="color-option gray"
|
|
450
520
|
@click="${(e: MouseEvent) =>
|
|
451
521
|
this.handleColorOptionClick(e, 'gray')}"
|
|
522
|
+
@touchend="${(e: TouchEvent) =>
|
|
523
|
+
this.handleColorOptionClick(e, 'gray')}"
|
|
452
524
|
></div>
|
|
453
525
|
</div>
|
|
454
526
|
</div>
|
|
@@ -19,6 +19,17 @@ export const send_msg: ActionConfig = {
|
|
|
19
19
|
render: (_node: Node, action: SendMsg) => {
|
|
20
20
|
const text = action.text.replace(/\n/g, '<br>');
|
|
21
21
|
return html`
|
|
22
|
+
${action.template
|
|
23
|
+
? html`<div
|
|
24
|
+
style="border: 1px solid #7dc8bc;padding: 0.5em;margin-bottom: 0.5em;border-radius: 4px; display:flex;align-items: flex-start;background: #f0faf7;color: #128C7E;font-size: 0.85em;"
|
|
25
|
+
>
|
|
26
|
+
<temba-icon
|
|
27
|
+
name="channel_wac"
|
|
28
|
+
style="--icon-size: 14px;"
|
|
29
|
+
></temba-icon>
|
|
30
|
+
<div style="margin-left:0.4em">${action.template.name}</div>
|
|
31
|
+
</div>`
|
|
32
|
+
: null}
|
|
22
33
|
${renderClamped(html`${unsafeHTML(text)}`, action.text)}
|
|
23
34
|
${(action.quick_replies || [])?.length > 0
|
|
24
35
|
? html`<div class="quick-replies">
|
|
@@ -27,14 +38,6 @@ export const send_msg: ActionConfig = {
|
|
|
27
38
|
})}
|
|
28
39
|
</div>`
|
|
29
40
|
: null}
|
|
30
|
-
${action.template
|
|
31
|
-
? html`<div
|
|
32
|
-
style="border: 1px solid var(--color-widget-border);padding: 0.5em;margin-top: 1em;border-radius: var(--curvature); display:flex;background: rgba(0,0,0,.03);"
|
|
33
|
-
>
|
|
34
|
-
<temba-icon name="channel_wac"></temba-icon>
|
|
35
|
-
<div style="margin-left:0.5em">${action.template.name}</div>
|
|
36
|
-
</div>`
|
|
37
|
-
: null}
|
|
38
41
|
`;
|
|
39
42
|
},
|
|
40
43
|
form: {
|
|
@@ -33,7 +33,9 @@ export const split_by_subflow: NodeConfig = {
|
|
|
33
33
|
) as any;
|
|
34
34
|
return html`
|
|
35
35
|
<div class="body">
|
|
36
|
-
${enterFlowAction?.flow
|
|
36
|
+
${enterFlowAction?.flow
|
|
37
|
+
? renderFlowLinks([enterFlowAction.flow], 'flow')
|
|
38
|
+
: null}
|
|
37
39
|
</div>
|
|
38
40
|
`;
|
|
39
41
|
},
|
package/src/flow/utils.ts
CHANGED
|
@@ -162,9 +162,7 @@ export const renderFlowLinks = (flows: NamedObject[], icon?: string) => {
|
|
|
162
162
|
itemElements.push(html`<div
|
|
163
163
|
style="display:flex;items-align:center;margin-top:0.2em;"
|
|
164
164
|
>
|
|
165
|
-
${icon
|
|
166
|
-
? html`<div style="margin-right:0.4em; width: 1em;"></div>`
|
|
167
|
-
: null}
|
|
165
|
+
${icon ? html`<div style="margin-right:0.4em; width: 1em;"></div>` : null}
|
|
168
166
|
<div style="font-size:0.8em">+${remainingCount} more</div>
|
|
169
167
|
</div>`);
|
|
170
168
|
}
|