@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/list/SortableList.ts
CHANGED
|
@@ -44,6 +44,7 @@ export class SortableList extends RapidElement {
|
|
|
44
44
|
|
|
45
45
|
slot > * {
|
|
46
46
|
user-select: none;
|
|
47
|
+
touch-action: none;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
temba-icon {
|
|
@@ -103,6 +104,9 @@ export class SortableList extends RapidElement {
|
|
|
103
104
|
this.handleMouseMove = this.handleMouseMove.bind(this);
|
|
104
105
|
this.handleMouseUp = this.handleMouseUp.bind(this);
|
|
105
106
|
this.handleMouseDown = this.handleMouseDown.bind(this);
|
|
107
|
+
this.handleTouchMove = this.handleTouchMove.bind(this);
|
|
108
|
+
this.handleTouchEnd = this.handleTouchEnd.bind(this);
|
|
109
|
+
this.handleTouchStart = this.handleTouchStart.bind(this);
|
|
106
110
|
}
|
|
107
111
|
|
|
108
112
|
private getSortableElements(): Element[] {
|
|
@@ -369,48 +373,81 @@ export class SortableList extends RapidElement {
|
|
|
369
373
|
this.downEle.insertAdjacentElement('afterend', this.dropPlaceholder);
|
|
370
374
|
}
|
|
371
375
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
376
|
+
/**
|
|
377
|
+
* Shared drag-start logic for both mouse and touch.
|
|
378
|
+
* Returns true if a drag target was found and state was initialised.
|
|
379
|
+
*/
|
|
380
|
+
private beginDrag(
|
|
381
|
+
target: HTMLElement,
|
|
382
|
+
clientX: number,
|
|
383
|
+
clientY: number
|
|
384
|
+
): boolean {
|
|
375
385
|
// if we have a drag handle, only allow dragging from that element
|
|
376
386
|
if (this.dragHandle) {
|
|
377
|
-
if (!
|
|
378
|
-
return;
|
|
387
|
+
if (!target.classList.contains(this.dragHandle)) {
|
|
388
|
+
return false;
|
|
379
389
|
}
|
|
380
390
|
}
|
|
381
391
|
|
|
382
|
-
ele =
|
|
383
|
-
if (ele)
|
|
392
|
+
const ele = target.closest('.sortable') as HTMLDivElement;
|
|
393
|
+
if (!ele) return false;
|
|
394
|
+
|
|
395
|
+
this.downEle = ele;
|
|
396
|
+
this.draggingId = ele.id;
|
|
397
|
+
this.draggingIdx = this.getRowIndex(ele.id);
|
|
398
|
+
this.draggingEle = ele;
|
|
399
|
+
|
|
400
|
+
const rect = ele.getBoundingClientRect();
|
|
401
|
+
this.originalElementRect = rect;
|
|
402
|
+
this.originalLayoutSize = {
|
|
403
|
+
width: ele.offsetWidth,
|
|
404
|
+
height: ele.offsetHeight
|
|
405
|
+
};
|
|
406
|
+
this.xOffset = clientX - rect.left;
|
|
407
|
+
this.yOffset = clientY - rect.top;
|
|
408
|
+
this.yDown = clientY;
|
|
409
|
+
this.xDown = clientX;
|
|
410
|
+
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
private handleMouseDown(event: MouseEvent) {
|
|
415
|
+
if (
|
|
416
|
+
this.beginDrag(event.target as HTMLElement, event.clientX, event.clientY)
|
|
417
|
+
) {
|
|
384
418
|
event.preventDefault();
|
|
385
419
|
event.stopPropagation();
|
|
386
|
-
this.downEle = ele;
|
|
387
|
-
this.draggingId = ele.id;
|
|
388
|
-
this.draggingIdx = this.getRowIndex(ele.id);
|
|
389
|
-
this.draggingEle = ele;
|
|
390
|
-
|
|
391
|
-
// Use getBoundingClientRect for accurate offsets and store original dimensions
|
|
392
|
-
const rect = ele.getBoundingClientRect();
|
|
393
|
-
this.originalElementRect = rect; // Store viewport rect for ghost positioning
|
|
394
|
-
this.originalLayoutSize = {
|
|
395
|
-
width: ele.offsetWidth,
|
|
396
|
-
height: ele.offsetHeight
|
|
397
|
-
}; // Store layout dimensions for placeholders
|
|
398
|
-
this.xOffset = event.clientX - rect.left;
|
|
399
|
-
this.yOffset = event.clientY - rect.top;
|
|
400
|
-
this.yDown = event.clientY;
|
|
401
|
-
this.xDown = event.clientX;
|
|
402
|
-
|
|
403
420
|
document.addEventListener('mousemove', this.handleMouseMove);
|
|
404
421
|
document.addEventListener('mouseup', this.handleMouseUp);
|
|
405
422
|
}
|
|
406
423
|
}
|
|
407
424
|
|
|
408
|
-
|
|
425
|
+
/* c8 ignore start -- touch-only handlers */
|
|
426
|
+
private handleTouchStart(event: TouchEvent) {
|
|
427
|
+
const touch = event.touches[0];
|
|
428
|
+
if (!touch) return;
|
|
429
|
+
if (
|
|
430
|
+
this.beginDrag(event.target as HTMLElement, touch.clientX, touch.clientY)
|
|
431
|
+
) {
|
|
432
|
+
event.stopPropagation();
|
|
433
|
+
document.addEventListener('touchmove', this.handleTouchMove, {
|
|
434
|
+
passive: false
|
|
435
|
+
});
|
|
436
|
+
document.addEventListener('touchend', this.handleTouchEnd);
|
|
437
|
+
document.addEventListener('touchcancel', this.handleTouchEnd);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/* c8 ignore stop */
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Shared drag-move logic for both mouse and touch.
|
|
444
|
+
*/
|
|
445
|
+
private processDragMove(clientX: number, clientY: number) {
|
|
409
446
|
if (
|
|
410
447
|
!this.ghostElement &&
|
|
411
448
|
this.downEle &&
|
|
412
|
-
(Math.abs(
|
|
413
|
-
Math.abs(
|
|
449
|
+
(Math.abs(clientY - this.yDown) > DRAG_THRESHOLD ||
|
|
450
|
+
Math.abs(clientX - this.xDown) > DRAG_THRESHOLD)
|
|
414
451
|
) {
|
|
415
452
|
this.fireCustomEvent(CustomEventType.DragStart, {
|
|
416
453
|
id: this.downEle.id
|
|
@@ -440,8 +477,8 @@ export class SortableList extends RapidElement {
|
|
|
440
477
|
const hasAncestorScale = Math.abs(ancestorScale - 1) > 0.001;
|
|
441
478
|
|
|
442
479
|
this.ghostElement.style.position = 'fixed';
|
|
443
|
-
this.ghostElement.style.left =
|
|
444
|
-
this.ghostElement.style.top =
|
|
480
|
+
this.ghostElement.style.left = clientX - this.xOffset + 'px';
|
|
481
|
+
this.ghostElement.style.top = clientY - this.yOffset + 'px';
|
|
445
482
|
this.ghostElement.style.zIndex = '99999';
|
|
446
483
|
this.ghostElement.style.opacity = '0.8';
|
|
447
484
|
|
|
@@ -480,12 +517,12 @@ export class SortableList extends RapidElement {
|
|
|
480
517
|
}
|
|
481
518
|
|
|
482
519
|
if (this.ghostElement) {
|
|
483
|
-
this.ghostElement.style.left =
|
|
484
|
-
this.ghostElement.style.top =
|
|
520
|
+
this.ghostElement.style.left = clientX - this.xOffset + 'px';
|
|
521
|
+
this.ghostElement.style.top = clientY - this.yOffset + 'px';
|
|
485
522
|
|
|
486
523
|
// check if the drag is over the container (only if external dragging is allowed)
|
|
487
524
|
const isOverContainer = this.externalDrag
|
|
488
|
-
? this.isMouseOverContainer(
|
|
525
|
+
? this.isMouseOverContainer(clientX, clientY)
|
|
489
526
|
: true; // always consider "over container" if external drag is disabled
|
|
490
527
|
|
|
491
528
|
// detect transition between internal and external drag (only if allowed)
|
|
@@ -501,8 +538,8 @@ export class SortableList extends RapidElement {
|
|
|
501
538
|
|
|
502
539
|
this.fireCustomEvent(CustomEventType.DragExternal, {
|
|
503
540
|
id: this.downEle.id,
|
|
504
|
-
mouseX:
|
|
505
|
-
mouseY:
|
|
541
|
+
mouseX: clientX,
|
|
542
|
+
mouseY: clientY
|
|
506
543
|
});
|
|
507
544
|
} else if (this.externalDrag && isOverContainer && this.isExternalDrag) {
|
|
508
545
|
// transitioning back to internal drag
|
|
@@ -520,7 +557,7 @@ export class SortableList extends RapidElement {
|
|
|
520
557
|
|
|
521
558
|
// only show drop placeholder and calculate drop position if internal drag
|
|
522
559
|
if (!this.isExternalDrag) {
|
|
523
|
-
const targetInfo = this.getDropTargetInfo(
|
|
560
|
+
const targetInfo = this.getDropTargetInfo(clientX, clientY);
|
|
524
561
|
if (targetInfo) {
|
|
525
562
|
const { element: targetElement, insertAfter } = targetInfo;
|
|
526
563
|
const targetIdx = this.getRowIndex(targetElement.id);
|
|
@@ -537,9 +574,6 @@ export class SortableList extends RapidElement {
|
|
|
537
574
|
dropIdx = insertAfter ? targetIdx + 1 : targetIdx;
|
|
538
575
|
} else {
|
|
539
576
|
// Target was originally after the drag position - moving forward
|
|
540
|
-
// When moving the dragged element forward (i.e., to a higher index), the targetIdx is based on the current DOM,
|
|
541
|
-
// which no longer includes the dragged element. This means all elements after the original position have shifted left by one,
|
|
542
|
-
// so we need to subtract 1 from targetIdx to get the correct insertion index. If inserting after the target, we use targetIdx as is.
|
|
543
577
|
dropIdx = insertAfter ? targetIdx : targetIdx - 1;
|
|
544
578
|
}
|
|
545
579
|
|
|
@@ -560,18 +594,30 @@ export class SortableList extends RapidElement {
|
|
|
560
594
|
// external drag - continue firing external drag events with updated position
|
|
561
595
|
this.fireCustomEvent(CustomEventType.DragExternal, {
|
|
562
596
|
id: this.downEle.id,
|
|
563
|
-
mouseX:
|
|
564
|
-
mouseY:
|
|
597
|
+
mouseX: clientX,
|
|
598
|
+
mouseY: clientY
|
|
565
599
|
});
|
|
566
600
|
}
|
|
567
601
|
}
|
|
568
602
|
}
|
|
569
603
|
|
|
570
|
-
private
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
604
|
+
private handleMouseMove(event: MouseEvent) {
|
|
605
|
+
this.processDragMove(event.clientX, event.clientY);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/* c8 ignore next 6 -- touch-only */
|
|
609
|
+
private handleTouchMove(event: TouchEvent) {
|
|
610
|
+
const touch = event.touches[0];
|
|
611
|
+
if (!touch) return;
|
|
612
|
+
event.preventDefault();
|
|
613
|
+
this.processDragMove(touch.clientX, touch.clientY);
|
|
614
|
+
}
|
|
574
615
|
|
|
616
|
+
/**
|
|
617
|
+
* Shared drag-end logic for both mouse and touch.
|
|
618
|
+
*/
|
|
619
|
+
private processDragEnd(clientX: number, clientY: number) {
|
|
620
|
+
if (this.draggingId && this.ghostElement) {
|
|
575
621
|
// Remove the ghost clone from document.body
|
|
576
622
|
if (this.ghostElement) {
|
|
577
623
|
this.ghostElement.remove();
|
|
@@ -610,8 +656,8 @@ export class SortableList extends RapidElement {
|
|
|
610
656
|
this.fireCustomEvent(CustomEventType.DragStop, {
|
|
611
657
|
id: this.draggingId,
|
|
612
658
|
isExternal: this.isExternalDrag,
|
|
613
|
-
mouseX:
|
|
614
|
-
mouseY:
|
|
659
|
+
mouseX: clientX,
|
|
660
|
+
mouseY: clientY
|
|
615
661
|
});
|
|
616
662
|
|
|
617
663
|
this.draggingId = null;
|
|
@@ -639,18 +685,42 @@ export class SortableList extends RapidElement {
|
|
|
639
685
|
}, 100);
|
|
640
686
|
}
|
|
641
687
|
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
private handleMouseUp(evt: MouseEvent) {
|
|
691
|
+
if (this.draggingId && this.ghostElement) {
|
|
692
|
+
evt.preventDefault();
|
|
693
|
+
evt.stopPropagation();
|
|
694
|
+
}
|
|
695
|
+
this.processDragEnd(evt.clientX, evt.clientY);
|
|
642
696
|
document.removeEventListener('mousemove', this.handleMouseMove);
|
|
643
697
|
document.removeEventListener('mouseup', this.handleMouseUp);
|
|
644
698
|
this.dispatchEvent(new Event('change'));
|
|
645
699
|
}
|
|
646
700
|
|
|
701
|
+
/* c8 ignore start -- touch-only */
|
|
702
|
+
private handleTouchEnd(evt: TouchEvent) {
|
|
703
|
+
const touch = evt.changedTouches[0];
|
|
704
|
+
const clientX = touch?.clientX ?? 0;
|
|
705
|
+
const clientY = touch?.clientY ?? 0;
|
|
706
|
+
this.processDragEnd(clientX, clientY);
|
|
707
|
+
document.removeEventListener('touchmove', this.handleTouchMove);
|
|
708
|
+
document.removeEventListener('touchend', this.handleTouchEnd);
|
|
709
|
+
document.removeEventListener('touchcancel', this.handleTouchEnd);
|
|
710
|
+
this.dispatchEvent(new Event('change'));
|
|
711
|
+
}
|
|
712
|
+
/* c8 ignore stop */
|
|
713
|
+
|
|
647
714
|
public render(): TemplateResult {
|
|
648
715
|
return html`
|
|
649
716
|
<div
|
|
650
717
|
class="container ${this.horizontal ? 'horizontal' : ''}"
|
|
651
718
|
style="gap: ${this.gap}"
|
|
652
719
|
>
|
|
653
|
-
<slot
|
|
720
|
+
<slot
|
|
721
|
+
@mousedown=${this.handleMouseDown}
|
|
722
|
+
@touchstart=${this.handleTouchStart}
|
|
723
|
+
></slot>
|
|
654
724
|
</div>
|
|
655
725
|
`;
|
|
656
726
|
}
|
|
@@ -582,6 +582,7 @@ export class Simulator extends RapidElement {
|
|
|
582
582
|
flex-wrap: wrap;
|
|
583
583
|
justify-content: center;
|
|
584
584
|
gap: 6px;
|
|
585
|
+
padding: 0 12px;
|
|
585
586
|
z-index: 9;
|
|
586
587
|
}
|
|
587
588
|
|
|
@@ -594,7 +595,10 @@ export class Simulator extends RapidElement {
|
|
|
594
595
|
font-size: 11px;
|
|
595
596
|
cursor: pointer;
|
|
596
597
|
transition: all 0.2s ease;
|
|
597
|
-
|
|
598
|
+
min-width: 0;
|
|
599
|
+
overflow: hidden;
|
|
600
|
+
text-overflow: ellipsis;
|
|
601
|
+
white-space: nowrap;
|
|
598
602
|
}
|
|
599
603
|
|
|
600
604
|
.quick-reply-btn:hover:not(:disabled) {
|