@nyaruka/temba-components 0.138.6 → 0.140.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/cla.yml +1 -1
- package/.github/workflows/copilot-setup-steps.yml +6 -1
- package/CHANGELOG.md +26 -0
- package/demo/data/flows/sample-flow.json +24 -0
- package/dist/locales/es.js +5 -5
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/fr.js +5 -5
- package/dist/locales/fr.js.map +1 -1
- package/dist/locales/locale-codes.js +2 -11
- package/dist/locales/locale-codes.js.map +1 -1
- package/dist/locales/pt.js +5 -5
- package/dist/locales/pt.js.map +1 -1
- package/dist/temba-components.js +1112 -882
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/display/Chat.js +10 -7
- package/out-tsc/src/display/Chat.js.map +1 -1
- package/out-tsc/src/display/Dropdown.js +3 -1
- package/out-tsc/src/display/Dropdown.js.map +1 -1
- package/out-tsc/src/display/FloatingTab.js +25 -32
- package/out-tsc/src/display/FloatingTab.js.map +1 -1
- package/out-tsc/src/display/Thumbnail.js +163 -5
- package/out-tsc/src/display/Thumbnail.js.map +1 -1
- package/out-tsc/src/flow/CanvasMenu.js +5 -3
- package/out-tsc/src/flow/CanvasMenu.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +70 -29
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +290 -239
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +118 -10
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/Plumber.js +757 -403
- package/out-tsc/src/flow/Plumber.js.map +1 -1
- package/out-tsc/src/flow/StickyNote.js +13 -4
- package/out-tsc/src/flow/StickyNote.js.map +1 -1
- package/out-tsc/src/flow/actions/audio-player.js +112 -0
- package/out-tsc/src/flow/actions/audio-player.js.map +1 -0
- package/out-tsc/src/flow/actions/enter_flow.js +43 -0
- package/out-tsc/src/flow/actions/enter_flow.js.map +1 -0
- package/out-tsc/src/flow/actions/play_audio.js +57 -4
- package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
- package/out-tsc/src/flow/actions/say_msg.js +86 -3
- package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
- package/out-tsc/src/flow/config.js +11 -3
- package/out-tsc/src/flow/config.js.map +1 -1
- package/out-tsc/src/flow/nodes/shared-rules.js +1 -1
- package/out-tsc/src/flow/nodes/shared-rules.js.map +1 -1
- package/out-tsc/src/flow/nodes/terminal.js +7 -0
- package/out-tsc/src/flow/nodes/terminal.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_audio.js +77 -0
- package/out-tsc/src/flow/nodes/wait_for_audio.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_dial.js +151 -0
- package/out-tsc/src/flow/nodes/wait_for_dial.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_digits.js +61 -1
- package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_menu.js +173 -2
- package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
- package/out-tsc/src/flow/operators.js +21 -5
- package/out-tsc/src/flow/operators.js.map +1 -1
- package/out-tsc/src/flow/types.js.map +1 -1
- package/out-tsc/src/flow/utils.js +213 -65
- package/out-tsc/src/flow/utils.js.map +1 -1
- package/out-tsc/src/form/ArrayEditor.js +4 -2
- package/out-tsc/src/form/ArrayEditor.js.map +1 -1
- package/out-tsc/src/form/FieldRenderer.js +49 -0
- package/out-tsc/src/form/FieldRenderer.js.map +1 -1
- package/out-tsc/src/interfaces.js +2 -0
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/layout/Dialog.js +52 -7
- package/out-tsc/src/layout/Dialog.js.map +1 -1
- package/out-tsc/src/list/TicketList.js +4 -1
- package/out-tsc/src/list/TicketList.js.map +1 -1
- package/out-tsc/src/live/TembaChart.js.map +1 -1
- package/out-tsc/src/locales/es.js +5 -5
- package/out-tsc/src/locales/es.js.map +1 -1
- package/out-tsc/src/locales/fr.js +5 -5
- package/out-tsc/src/locales/fr.js.map +1 -1
- package/out-tsc/src/locales/locale-codes.js +2 -11
- package/out-tsc/src/locales/locale-codes.js.map +1 -1
- package/out-tsc/src/locales/pt.js +5 -5
- package/out-tsc/src/locales/pt.js.map +1 -1
- package/out-tsc/src/simulator/Simulator.js +10 -3
- package/out-tsc/src/simulator/Simulator.js.map +1 -1
- package/out-tsc/src/store/AppState.js +89 -3
- package/out-tsc/src/store/AppState.js.map +1 -1
- package/out-tsc/test/actions/play_audio.test.js +118 -0
- package/out-tsc/test/actions/play_audio.test.js.map +1 -0
- package/out-tsc/test/actions/say_msg.test.js +158 -0
- package/out-tsc/test/actions/say_msg.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_audio.test.js +156 -0
- package/out-tsc/test/nodes/wait_for_audio.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_dial.test.js +336 -0
- package/out-tsc/test/nodes/wait_for_dial.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_digits.test.js +198 -84
- package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
- package/out-tsc/test/nodes/wait_for_menu.test.js +340 -0
- package/out-tsc/test/nodes/wait_for_menu.test.js.map +1 -0
- package/out-tsc/test/temba-floating-tab.test.js +4 -6
- package/out-tsc/test/temba-floating-tab.test.js.map +1 -1
- package/out-tsc/test/temba-flow-collision.test.js +473 -220
- package/out-tsc/test/temba-flow-collision.test.js.map +1 -1
- package/out-tsc/test/temba-flow-editor.test.js +0 -2
- package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
- package/out-tsc/test/temba-flow-plumber-connections.test.js +83 -84
- package/out-tsc/test/temba-flow-plumber-connections.test.js.map +1 -1
- package/out-tsc/test/temba-flow-plumber.test.js +102 -93
- package/out-tsc/test/temba-flow-plumber.test.js.map +1 -1
- package/out-tsc/test/temba-node-type-selector.test.js +6 -6
- package/out-tsc/test/temba-node-type-selector.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/actions/play_audio/editor/expression-url.png +0 -0
- package/screenshots/truth/actions/play_audio/editor/static-url.png +0 -0
- package/screenshots/truth/actions/play_audio/render/expression-url.png +0 -0
- package/screenshots/truth/actions/play_audio/render/static-url.png +0 -0
- package/screenshots/truth/actions/say_msg/editor/multiline-text.png +0 -0
- package/screenshots/truth/actions/say_msg/editor/simple-text.png +0 -0
- package/screenshots/truth/actions/say_msg/editor/text-with-audio-url.png +0 -0
- package/screenshots/truth/actions/say_msg/render/multiline-text.png +0 -0
- package/screenshots/truth/actions/say_msg/render/simple-text.png +0 -0
- package/screenshots/truth/actions/say_msg/render/text-with-audio-url.png +0 -0
- package/screenshots/truth/editor/router.png +0 -0
- package/screenshots/truth/editor/wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_audio/editor/basic-audio-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_audio/render/basic-audio-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_dial/editor/basic-dial.png +0 -0
- package/screenshots/truth/nodes/wait_for_dial/editor/dial-with-limits.png +0 -0
- package/screenshots/truth/nodes/wait_for_dial/render/basic-dial.png +0 -0
- package/screenshots/truth/nodes/wait_for_dial/render/dial-with-limits.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/digits-with-rules.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/digits-with-rules.png +0 -0
- package/screenshots/truth/nodes/wait_for_menu/editor/menu-with-digits.png +0 -0
- package/screenshots/truth/nodes/wait_for_menu/render/menu-with-digits.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
- package/src/display/Chat.ts +13 -7
- package/src/display/Dropdown.ts +3 -1
- package/src/display/FloatingTab.ts +24 -33
- package/src/display/Thumbnail.ts +162 -2
- package/src/flow/CanvasMenu.ts +8 -3
- package/src/flow/CanvasNode.ts +75 -30
- package/src/flow/Editor.ts +336 -288
- package/src/flow/NodeEditor.ts +137 -9
- package/src/flow/Plumber.ts +1011 -457
- package/src/flow/StickyNote.ts +14 -4
- package/src/flow/actions/audio-player.ts +127 -0
- package/src/flow/actions/enter_flow.ts +44 -0
- package/src/flow/actions/play_audio.ts +64 -5
- package/src/flow/actions/say_msg.ts +94 -4
- package/src/flow/config.ts +11 -3
- package/src/flow/nodes/shared-rules.ts +1 -1
- package/src/flow/nodes/terminal.ts +9 -0
- package/src/flow/nodes/wait_for_audio.ts +88 -0
- package/src/flow/nodes/wait_for_dial.ts +176 -0
- package/src/flow/nodes/wait_for_digits.ts +86 -2
- package/src/flow/nodes/wait_for_menu.ts +209 -3
- package/src/flow/operators.ts +23 -5
- package/src/flow/types.ts +23 -1
- package/src/flow/utils.ts +238 -81
- package/src/form/ArrayEditor.ts +4 -2
- package/src/form/FieldRenderer.ts +64 -1
- package/src/interfaces.ts +3 -1
- package/src/layout/Dialog.ts +53 -7
- package/src/list/TicketList.ts +4 -1
- package/src/live/TembaChart.ts +1 -1
- package/src/locales/es.ts +13 -18
- package/src/locales/fr.ts +13 -18
- package/src/locales/locale-codes.ts +2 -11
- package/src/locales/pt.ts +13 -18
- package/src/simulator/Simulator.ts +13 -3
- package/src/store/AppState.ts +105 -1
- package/src/store/flow-definition.d.ts +2 -0
- package/test/actions/play_audio.test.ts +155 -0
- package/test/actions/say_msg.test.ts +196 -0
- package/test/nodes/wait_for_audio.test.ts +182 -0
- package/test/nodes/wait_for_dial.test.ts +382 -0
- package/test/nodes/wait_for_digits.test.ts +233 -109
- package/test/nodes/wait_for_menu.test.ts +383 -0
- package/test/temba-floating-tab.test.ts +4 -6
- package/test/temba-flow-collision.test.ts +495 -293
- package/test/temba-flow-editor.test.ts +0 -2
- package/test/temba-flow-plumber-connections.test.ts +97 -97
- package/test/temba-flow-plumber.test.ts +116 -103
- package/test/temba-node-type-selector.test.ts +6 -6
- package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
2
|
import { css, html } from 'lit';
|
|
3
3
|
import { RapidElement } from '../RapidElement';
|
|
4
|
-
import { property } from 'lit/decorators.js';
|
|
4
|
+
import { property, state } from 'lit/decorators.js';
|
|
5
5
|
import { getClasses } from '../utils';
|
|
6
6
|
import { WebChatIcon } from '../webchat';
|
|
7
7
|
var ThumbnailContentType;
|
|
@@ -27,6 +27,11 @@ export class Thumbnail extends RapidElement {
|
|
|
27
27
|
this.preview = true;
|
|
28
28
|
// cached tile URL for location thumbnails
|
|
29
29
|
this.tileUrl = '';
|
|
30
|
+
// audio player state
|
|
31
|
+
this.audio = null;
|
|
32
|
+
this.audioPlaying = false;
|
|
33
|
+
this.audioProgress = 0;
|
|
34
|
+
this.audioDuration = 0;
|
|
30
35
|
}
|
|
31
36
|
static get styles() {
|
|
32
37
|
return css `
|
|
@@ -72,11 +77,57 @@ export class Thumbnail extends RapidElement {
|
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
.thumb.document,
|
|
75
|
-
.thumb.audio,
|
|
76
80
|
.thumb.video {
|
|
77
81
|
border: 1px solid #eee;
|
|
78
82
|
}
|
|
79
83
|
|
|
84
|
+
.audio-player {
|
|
85
|
+
display: flex;
|
|
86
|
+
align-items: center;
|
|
87
|
+
gap: 6px;
|
|
88
|
+
padding: 6px 8px;
|
|
89
|
+
background: rgba(0, 0, 0, 0.05);
|
|
90
|
+
border-radius: var(--curvature);
|
|
91
|
+
cursor: default;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.audio-play-btn {
|
|
95
|
+
cursor: pointer;
|
|
96
|
+
color: #666;
|
|
97
|
+
display: flex;
|
|
98
|
+
align-items: center;
|
|
99
|
+
flex-shrink: 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.audio-play-btn:hover {
|
|
103
|
+
color: #333;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.audio-progress-bar {
|
|
107
|
+
flex: 1;
|
|
108
|
+
height: 3px;
|
|
109
|
+
background: #ddd;
|
|
110
|
+
border-radius: 2px;
|
|
111
|
+
overflow: hidden;
|
|
112
|
+
min-width: 60px;
|
|
113
|
+
cursor: pointer;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.audio-progress-fill {
|
|
117
|
+
height: 100%;
|
|
118
|
+
background: var(--color-primary, #2387ca);
|
|
119
|
+
border-radius: 2px;
|
|
120
|
+
transition: width 0.15s linear;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.audio-time {
|
|
124
|
+
font-size: 11px;
|
|
125
|
+
color: #999;
|
|
126
|
+
flex-shrink: 0;
|
|
127
|
+
min-width: 28px;
|
|
128
|
+
text-align: right;
|
|
129
|
+
}
|
|
130
|
+
|
|
80
131
|
.wrapper:hover .thumb.icon {
|
|
81
132
|
}
|
|
82
133
|
|
|
@@ -109,6 +160,58 @@ export class Thumbnail extends RapidElement {
|
|
|
109
160
|
}
|
|
110
161
|
`;
|
|
111
162
|
}
|
|
163
|
+
handleAudioPlayClick(e) {
|
|
164
|
+
e.stopPropagation();
|
|
165
|
+
if (!this.audio) {
|
|
166
|
+
this.audio = new Audio(this.url);
|
|
167
|
+
this.audio.addEventListener('timeupdate', () => {
|
|
168
|
+
if (this.audio.duration) {
|
|
169
|
+
this.audioProgress = this.audio.currentTime / this.audio.duration;
|
|
170
|
+
this.audioDuration = this.audio.duration;
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
this.audio.addEventListener('ended', () => {
|
|
174
|
+
this.audioPlaying = false;
|
|
175
|
+
this.audioProgress = 0;
|
|
176
|
+
});
|
|
177
|
+
this.audio.addEventListener('error', () => {
|
|
178
|
+
this.audioPlaying = false;
|
|
179
|
+
this.audioProgress = 0;
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
if (this.audioPlaying) {
|
|
183
|
+
this.audio.pause();
|
|
184
|
+
this.audioPlaying = false;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
this.audio.play().catch(() => {
|
|
188
|
+
this.audioPlaying = false;
|
|
189
|
+
});
|
|
190
|
+
this.audioPlaying = true;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
handleProgressClick(e) {
|
|
194
|
+
e.stopPropagation();
|
|
195
|
+
if (!this.audio || !this.audio.duration)
|
|
196
|
+
return;
|
|
197
|
+
const bar = e.currentTarget;
|
|
198
|
+
const rect = bar.getBoundingClientRect();
|
|
199
|
+
const pct = (e.clientX - rect.left) / rect.width;
|
|
200
|
+
this.audio.currentTime = pct * this.audio.duration;
|
|
201
|
+
}
|
|
202
|
+
formatTime(seconds) {
|
|
203
|
+
const s = Math.floor(seconds);
|
|
204
|
+
const m = Math.floor(s / 60);
|
|
205
|
+
const rem = s % 60;
|
|
206
|
+
return `${m}:${rem.toString().padStart(2, '0')}`;
|
|
207
|
+
}
|
|
208
|
+
disconnectedCallback() {
|
|
209
|
+
super.disconnectedCallback();
|
|
210
|
+
if (this.audio) {
|
|
211
|
+
this.audio.pause();
|
|
212
|
+
this.audio = null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
112
215
|
// convert lat/lng to tile coordinates for OSM
|
|
113
216
|
latLngToTile(lat, lng, zoom) {
|
|
114
217
|
const n = Math.pow(2, zoom);
|
|
@@ -198,6 +301,9 @@ export class Thumbnail extends RapidElement {
|
|
|
198
301
|
const osmUrl = `https://www.openstreetmap.org/?mlat=${this.latitude}&mlon=${this.longitude}#map=15/${this.latitude}/${this.longitude}`;
|
|
199
302
|
window.open(osmUrl, '_blank');
|
|
200
303
|
}
|
|
304
|
+
else if (this.contentType === ThumbnailContentType.AUDIO) {
|
|
305
|
+
// audio has inline controls, no click action needed
|
|
306
|
+
}
|
|
201
307
|
else {
|
|
202
308
|
window.open(this.url, '_blank');
|
|
203
309
|
}
|
|
@@ -209,10 +315,14 @@ export class Thumbnail extends RapidElement {
|
|
|
209
315
|
window.open(this.url, '_blank');
|
|
210
316
|
}
|
|
211
317
|
render() {
|
|
318
|
+
var _a;
|
|
212
319
|
return html `
|
|
213
320
|
<div
|
|
214
321
|
@click=${this.handleThumbnailClicked.bind(this)}
|
|
215
322
|
class="${getClasses({ wrapper: true, zoom: this.zoom })}"
|
|
323
|
+
style="${this.contentType === ThumbnailContentType.AUDIO
|
|
324
|
+
? 'cursor: default;'
|
|
325
|
+
: ''}"
|
|
216
326
|
url=${this.url}
|
|
217
327
|
>
|
|
218
328
|
${this.contentType === ThumbnailContentType.IMAGE && this.preview
|
|
@@ -220,14 +330,53 @@ export class Thumbnail extends RapidElement {
|
|
|
220
330
|
class="observe thumb ${this.contentType}"
|
|
221
331
|
src="${this.url}"
|
|
222
332
|
></img></div>`
|
|
223
|
-
:
|
|
333
|
+
: this.contentType === ThumbnailContentType.AUDIO
|
|
334
|
+
? html `<div class="audio-player">
|
|
335
|
+
<div class="audio-play-btn" @click=${this.handleAudioPlayClick}>
|
|
336
|
+
${this.audioPlaying
|
|
337
|
+
? html `<svg
|
|
338
|
+
viewBox="0 0 24 24"
|
|
339
|
+
width="14"
|
|
340
|
+
height="14"
|
|
341
|
+
fill="currentColor"
|
|
342
|
+
>
|
|
343
|
+
<rect x="5" y="3" width="4" height="18" />
|
|
344
|
+
<rect x="15" y="3" width="4" height="18" />
|
|
345
|
+
</svg>`
|
|
346
|
+
: html `<svg
|
|
347
|
+
viewBox="0 0 24 24"
|
|
348
|
+
width="14"
|
|
349
|
+
height="14"
|
|
350
|
+
fill="currentColor"
|
|
351
|
+
>
|
|
352
|
+
<polygon points="6,3 20,12 6,21" />
|
|
353
|
+
</svg>`}
|
|
354
|
+
</div>
|
|
355
|
+
<div
|
|
356
|
+
class="audio-progress-bar"
|
|
357
|
+
@click=${this.handleProgressClick}
|
|
358
|
+
>
|
|
359
|
+
<div
|
|
360
|
+
class="audio-progress-fill"
|
|
361
|
+
style="width: ${this.audioProgress * 100}%"
|
|
362
|
+
></div>
|
|
363
|
+
</div>
|
|
364
|
+
<div class="audio-time">
|
|
365
|
+
${this.audioDuration
|
|
366
|
+
? this.formatTime(this.audioPlaying || this.audioProgress > 0
|
|
367
|
+
? ((_a = this.audio) === null || _a === void 0 ? void 0 : _a.currentTime) || 0
|
|
368
|
+
: this.audioDuration)
|
|
369
|
+
: ''}
|
|
370
|
+
</div>
|
|
371
|
+
</div>`
|
|
372
|
+
: html `
|
|
224
373
|
${this.contentType === ThumbnailContentType.LOCATION
|
|
225
|
-
|
|
374
|
+
? html `<img
|
|
226
375
|
style="height:125px;margin-bottom:-3px;border-radius:var(--curvature);"
|
|
227
376
|
src="${this.tileUrl}"
|
|
228
377
|
alt="Location preview"
|
|
229
378
|
/>`
|
|
230
|
-
|
|
379
|
+
: html `<div
|
|
231
380
|
style="padding:1em; background:rgba(0,0,0,.05);border-radius:var(--curvature);"
|
|
232
381
|
>
|
|
233
382
|
<temba-icon
|
|
@@ -267,4 +416,13 @@ __decorate([
|
|
|
267
416
|
__decorate([
|
|
268
417
|
property({ type: String, attribute: false })
|
|
269
418
|
], Thumbnail.prototype, "tileUrl", void 0);
|
|
419
|
+
__decorate([
|
|
420
|
+
state()
|
|
421
|
+
], Thumbnail.prototype, "audioPlaying", void 0);
|
|
422
|
+
__decorate([
|
|
423
|
+
state()
|
|
424
|
+
], Thumbnail.prototype, "audioProgress", void 0);
|
|
425
|
+
__decorate([
|
|
426
|
+
state()
|
|
427
|
+
], Thumbnail.prototype, "audioDuration", void 0);
|
|
270
428
|
//# sourceMappingURL=Thumbnail.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Thumbnail.js","sourceRoot":"","sources":["../../../src/display/Thumbnail.ts"],"names":[],"mappings":";AAAA,OAAO,EAAoB,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,IAAK,oBAOJ;AAPD,WAAK,oBAAoB;IACvB,uCAAe,CAAA;IACf,uCAAe,CAAA;IACf,uCAAe,CAAA;IACf,6CAAqB,CAAA;IACrB,6CAAqB,CAAA;IACrB,uCAAe,CAAA;AACjB,CAAC,EAPI,oBAAoB,KAApB,oBAAoB,QAOxB;AAED,MAAM,cAAc,GAAG;IACrB,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,gBAAgB;IAC1D,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,gBAAgB;IAC1D,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,gBAAgB;IAC1D,CAAC,oBAAoB,CAAC,QAAQ,CAAC,EAAE,WAAW,CAAC,mBAAmB;IAChE,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,UAAU;CACrD,CAAC;AAEF,MAAM,OAAO,SAAU,SAAQ,YAAY;IAA3C;;QA0FE,UAAK,GAAW,CAAC,CAAC;QAGlB,YAAO,GAAY,IAAI,CAAC;QAcxB,0CAA0C;QAElC,YAAO,GAAW,EAAE,CAAC;IA8I/B,CAAC;IA1PC,MAAM,KAAK,MAAM;QACf,OAAO,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA8ET,CAAC;IACJ,CAAC;IA8BD,8CAA8C;IACtC,YAAY,CAAC,GAAW,EAAE,GAAW,EAAE,IAAY;QACzD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAC5B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAClB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;YACrE,CAAC,CACJ,CAAC;QACF,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAES,OAAO,CACf,OAA0D;QAE1D,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAEvB,IACE,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;YAC1B,IAAI,CAAC,WAAW,KAAK,oBAAoB,CAAC,KAAK,EAC/C,CAAC;YACD,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YAC5D,IAAI,SAAS,EAAE,CAAC;gBACd,IAAI,cAAc,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE;oBACjC,IAAI,SAAS,CAAC,YAAY,GAAG,CAAC,IAAI,SAAS,CAAC,WAAW,GAAG,CAAC,EAAE,CAAC;wBAC5D,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,YAAY,GAAG,SAAS,CAAC,WAAW,CAAC;wBAC5D,IAAI,CAAC,OAAO;4BACV,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,IAAI,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC;wBAC/D,QAAQ,CAAC,UAAU,EAAE,CAAC;oBACxB,CAAC;gBACH,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACxB,CAAC;QACH,CAAC;QAED,4CAA4C;QAC5C,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;YAC9B,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBACpB,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAChD,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;oBACtB,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC;gBAC7B,CAAC;qBAAM,CAAC;oBACN,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;oBAC7D,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;oBAErD,IAAI,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;wBACpC,IAAI,CAAC,WAAW,GAAG,oBAAoB,CAAC,KAAK,CAAC;oBAChD,CAAC;yBAAM,IAAI,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;wBAC3C,IAAI,CAAC,WAAW,GAAG,oBAAoB,CAAC,KAAK,CAAC;oBAChD,CAAC;yBAAM,IAAI,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;wBAC3C,IAAI,CAAC,WAAW,GAAG,oBAAoB,CAAC,KAAK,CAAC;oBAChD,CAAC;yBAAM,IAAI,WAAW,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;wBACjD,IAAI,CAAC,WAAW,GAAG,oBAAoB,CAAC,QAAQ,CAAC;oBACnD,CAAC;yBAAM,IAAI,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;wBACzC,IAAI,CAAC,WAAW,GAAG,oBAAoB,CAAC,QAAQ,CAAC;wBACjD,wEAAwE;wBACxE,8BAA8B;wBAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;wBAClD,IAAI,MAAM,EAAE,CAAC;4BACX,IAAI,CAAC,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;4BACtC,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;wBACzC,CAAC;oBACH,CAAC;yBAAM,CAAC;wBACN,IAAI,CAAC,WAAW,GAAG,oBAAoB,CAAC,KAAK,CAAC;oBAChD,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,qDAAqD;QACrD,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YACxD,IACE,IAAI,CAAC,QAAQ,KAAK,SAAS;gBAC3B,IAAI,CAAC,SAAS,KAAK,SAAS;gBAC5B,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;gBACrB,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EACtB,CAAC;gBACD,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;gBAClE,IAAI,CAAC,OAAO,GAAG,kCAAkC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC;YACpF,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;YACpB,CAAC;QACH,CAAC;IACH,CAAC;IAEM,sBAAsB;QAC3B,IAAI,IAAI,CAAC,WAAW,KAAK,oBAAoB,CAAC,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACpE,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE;gBACrB,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,gBAAgB,CAAa,CAAC;gBACtE,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAC7B,CAAC,EAAE,GAAG,CAAC,CAAC;QACV,CAAC;aAAM,IAAI,IAAI,CAAC,WAAW,KAAK,oBAAoB,CAAC,QAAQ,EAAE,CAAC;YAC9D,iCAAiC;YACjC,MAAM,MAAM,GAAG,uCAAuC,IAAI,CAAC,QAAQ,SAAS,IAAI,CAAC,SAAS,WAAW,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACvI,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAEM,cAAc,CAAC,CAAQ;QAC5B,CAAC,CAAC,eAAe,EAAE,CAAC;QACpB,CAAC,CAAC,cAAc,EAAE,CAAC;QAEnB,+BAA+B;QAC/B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAClC,CAAC;IAEM,MAAM;QACX,OAAO,IAAI,CAAA;;iBAEE,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC;iBACtC,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;cACjD,IAAI,CAAC,GAAG;;UAEZ,IAAI,CAAC,WAAW,KAAK,oBAAoB,CAAC,KAAK,IAAI,IAAI,CAAC,OAAO;YAC/D,CAAC,CAAC,IAAI,CAAA,8CAA8C,IAAI,CAAC,cAAc,CAAC,IAAI,CACxE,IAAI,CACL;iCACoB,IAAI,CAAC,WAAW;iBAChC,IAAI,CAAC,GAAG;sBACH;YACZ,CAAC,CAAC,IAAI,CAAA;gBACA,IAAI,CAAC,WAAW,KAAK,oBAAoB,CAAC,QAAQ;gBAClD,CAAC,CAAC,IAAI,CAAA;;2BAEK,IAAI,CAAC,OAAO;;qBAElB;gBACL,CAAC,CAAC,IAAI,CAAA;;;;;8BAKQ,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC;;yBAErC;aACZ;;KAER,CAAC;IACJ,CAAC;CACF;AAvKC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;sCACf;AAGZ;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;6CACR;AAGnB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;wCACT;AAGlB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;0CACJ;AAGxB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;uCAChC;AAGd;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;8CACxB;AAGpB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;2CAC5B;AAGjB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;4CAC3B;AAIV;IADP,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;0CAChB","sourcesContent":["import { PropertyValueMap, css, html } from 'lit';\nimport { RapidElement } from '../RapidElement';\nimport { property } from 'lit/decorators.js';\nimport { getClasses } from '../utils';\nimport { Lightbox } from './Lightbox';\nimport { WebChatIcon } from '../webchat';\n\nenum ThumbnailContentType {\n IMAGE = 'image',\n AUDIO = 'audio',\n VIDEO = 'video',\n DOCUMENT = 'document',\n LOCATION = 'location',\n OTHER = 'other'\n}\n\nconst ThumbnailIcons = {\n [ThumbnailContentType.IMAGE]: WebChatIcon.attachment_image,\n [ThumbnailContentType.AUDIO]: WebChatIcon.attachment_audio,\n [ThumbnailContentType.VIDEO]: WebChatIcon.attachment_video,\n [ThumbnailContentType.DOCUMENT]: WebChatIcon.attachment_document,\n [ThumbnailContentType.OTHER]: WebChatIcon.attachment\n};\n\nexport class Thumbnail extends RapidElement {\n static get styles() {\n return css`\n :host {\n display: inline;\n }\n\n .wrapper {\n padding: var(--thumb-padding, 0.4em);\n background: var(--thumb-background, #fff);\n box-shadow: var(--widget-box-shadow);\n cursor: pointer;\n border-radius: calc(var(--curvature) * 1.5);\n border: 0px solid #f3f3f3;\n }\n\n .wrapper.zoom {\n border: none;\n padding: 0 !important;\n border-radius: 0 !important;\n overflow: hidden !important;\n }\n\n .zoom .thumb {\n border-radius: 0px !important;\n width: calc(var(--thumb-size, 4em) + 0.8em);\n max-height: calc(90vh - 10em);\n }\n\n .thumb {\n background-size: cover;\n background-position: center;\n background-repeat: no-repeat;\n border-radius: var(--curvature);\n max-height: calc(var(--thumb-size, 4em) * 2);\n max-width: calc(var(--thumb-size, 4em) * 2);\n height: var(--thumb-size, 4em);\n display: flex;\n align-items: center;\n justify-content: center;\n font-weight: 400;\n color: var(--thumb-icon, #bbb);\n }\n\n .thumb.document,\n .thumb.audio,\n .thumb.video {\n border: 1px solid #eee;\n }\n\n .wrapper:hover .thumb.icon {\n }\n\n .viewer {\n display: block;\n }\n\n .zoom .viewer {\n display: block;\n }\n\n .download {\n display: none;\n position: absolute;\n right: 0em;\n bottom: 0em;\n border-radius: var(--curvature);\n transform: scale(0.2) translate(3em, 3em);\n padding: 0.4em;\n }\n\n .zoom .download {\n display: block;\n background: rgba(0, 0, 0, 0.5);\n }\n\n .zoom .download:hover {\n background: rgba(0, 0, 0, 0.6);\n cursor: pointer;\n }\n `;\n }\n\n @property({ type: String })\n url: string;\n\n @property({ type: String })\n attachment: string;\n\n @property({ type: Number })\n ratio: number = 0;\n\n @property({ type: Boolean })\n preview: boolean = true;\n\n @property({ type: Boolean, attribute: false })\n zoom: boolean;\n\n @property({ type: String, attribute: true })\n contentType: string;\n\n @property({ type: Number, attribute: false })\n latitude: number;\n\n @property({ type: Number, attribute: false })\n longitude: number;\n\n // cached tile URL for location thumbnails\n @property({ type: String, attribute: false })\n private tileUrl: string = '';\n\n // convert lat/lng to tile coordinates for OSM\n private latLngToTile(lat: number, lng: number, zoom: number) {\n const n = Math.pow(2, zoom);\n const x = Math.floor(((lng + 180) / 360) * n);\n const latRad = (lat * Math.PI) / 180;\n const y = Math.floor(\n ((1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2) *\n n\n );\n return { x, y, z: zoom };\n }\n\n protected updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n\n if (\n changes.has('contentType') &&\n this.contentType === ThumbnailContentType.IMAGE\n ) {\n const toObserve = this.shadowRoot.querySelector('.observe');\n if (toObserve) {\n new ResizeObserver((e, observer) => {\n if (toObserve.clientHeight > 0 && toObserve.clientWidth > 0) {\n this.ratio = toObserve.clientHeight / toObserve.clientWidth;\n this.preview =\n this.ratio === 0 || (this.ratio > 0.25 && this.ratio <= 1.5);\n observer.disconnect();\n }\n }).observe(toObserve);\n }\n }\n\n // convert our attachment to a url and label\n if (changes.has('attachment')) {\n if (this.attachment) {\n const splitIndex = this.attachment.indexOf(':');\n if (splitIndex === -1) {\n this.url = this.attachment;\n } else {\n const contentType = this.attachment.substring(0, splitIndex);\n this.url = this.attachment.substring(splitIndex + 1);\n\n if (contentType.startsWith('image')) {\n this.contentType = ThumbnailContentType.IMAGE;\n } else if (contentType.startsWith('audio')) {\n this.contentType = ThumbnailContentType.AUDIO;\n } else if (contentType.startsWith('video')) {\n this.contentType = ThumbnailContentType.VIDEO;\n } else if (contentType.startsWith('application')) {\n this.contentType = ThumbnailContentType.DOCUMENT;\n } else if (contentType.startsWith('geo')) {\n this.contentType = ThumbnailContentType.LOCATION;\n // Parse coordinates from URL which is already stripped of \"geo:\" prefix\n // Format is now just: lat,lng\n const coords = this.url.match(/^([^,]+),([^,]+)/);\n if (coords) {\n this.latitude = parseFloat(coords[1]);\n this.longitude = parseFloat(coords[2]);\n }\n } else {\n this.contentType = ThumbnailContentType.OTHER;\n }\n }\n }\n }\n\n // calculate tile URL when latitude/longitude changes\n if (changes.has('latitude') || changes.has('longitude')) {\n if (\n this.latitude !== undefined &&\n this.longitude !== undefined &&\n !isNaN(this.latitude) &&\n !isNaN(this.longitude)\n ) {\n const tile = this.latLngToTile(this.latitude, this.longitude, 13);\n this.tileUrl = `https://tile.openstreetmap.org/${tile.z}/${tile.x}/${tile.y}.png`;\n } else {\n this.tileUrl = '';\n }\n }\n }\n\n public handleThumbnailClicked() {\n if (this.contentType === ThumbnailContentType.IMAGE && this.preview) {\n window.setTimeout(() => {\n const lightbox = document.querySelector('temba-lightbox') as Lightbox;\n lightbox.showElement(this);\n }, 100);\n } else if (this.contentType === ThumbnailContentType.LOCATION) {\n // open location in openstreetmap\n const osmUrl = `https://www.openstreetmap.org/?mlat=${this.latitude}&mlon=${this.longitude}#map=15/${this.latitude}/${this.longitude}`;\n window.open(osmUrl, '_blank');\n } else {\n window.open(this.url, '_blank');\n }\n }\n\n public handleDownload(e: Event) {\n e.stopPropagation();\n e.preventDefault();\n\n // open this.url in another tab\n window.open(this.url, '_blank');\n }\n\n public render() {\n return html`\n <div\n @click=${this.handleThumbnailClicked.bind(this)}\n class=\"${getClasses({ wrapper: true, zoom: this.zoom })}\"\n url=${this.url}\n >\n ${this.contentType === ThumbnailContentType.IMAGE && this.preview\n ? html`<div class=\"\"><div class=\"download\" @click=${this.handleDownload.bind(\n this\n )}><temba-icon size=\"1\" style=\"color:#fff;\" name=\"download\"></temba-icon></div><img\n class=\"observe thumb ${this.contentType}\"\n src=\"${this.url}\"\n ></img></div>`\n : html`\n ${this.contentType === ThumbnailContentType.LOCATION\n ? html`<img\n style=\"height:125px;margin-bottom:-3px;border-radius:var(--curvature);\"\n src=\"${this.tileUrl}\"\n alt=\"Location preview\"\n />`\n : html`<div\n style=\"padding:1em; background:rgba(0,0,0,.05);border-radius:var(--curvature);\"\n >\n <temba-icon\n size=\"1.5\"\n name=\"${ThumbnailIcons[this.contentType]}\"\n ></temba-icon>\n </div>`}\n `}\n </div>\n `;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"Thumbnail.js","sourceRoot":"","sources":["../../../src/display/Thumbnail.ts"],"names":[],"mappings":"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","sourcesContent":["import { PropertyValueMap, css, html } from 'lit';\nimport { RapidElement } from '../RapidElement';\nimport { property, state } from 'lit/decorators.js';\nimport { getClasses } from '../utils';\nimport { Lightbox } from './Lightbox';\nimport { WebChatIcon } from '../webchat';\n\nenum ThumbnailContentType {\n IMAGE = 'image',\n AUDIO = 'audio',\n VIDEO = 'video',\n DOCUMENT = 'document',\n LOCATION = 'location',\n OTHER = 'other'\n}\n\nconst ThumbnailIcons = {\n [ThumbnailContentType.IMAGE]: WebChatIcon.attachment_image,\n [ThumbnailContentType.AUDIO]: WebChatIcon.attachment_audio,\n [ThumbnailContentType.VIDEO]: WebChatIcon.attachment_video,\n [ThumbnailContentType.DOCUMENT]: WebChatIcon.attachment_document,\n [ThumbnailContentType.OTHER]: WebChatIcon.attachment\n};\n\nexport class Thumbnail extends RapidElement {\n static get styles() {\n return css`\n :host {\n display: inline;\n }\n\n .wrapper {\n padding: var(--thumb-padding, 0.4em);\n background: var(--thumb-background, #fff);\n box-shadow: var(--widget-box-shadow);\n cursor: pointer;\n border-radius: calc(var(--curvature) * 1.5);\n border: 0px solid #f3f3f3;\n }\n\n .wrapper.zoom {\n border: none;\n padding: 0 !important;\n border-radius: 0 !important;\n overflow: hidden !important;\n }\n\n .zoom .thumb {\n border-radius: 0px !important;\n width: calc(var(--thumb-size, 4em) + 0.8em);\n max-height: calc(90vh - 10em);\n }\n\n .thumb {\n background-size: cover;\n background-position: center;\n background-repeat: no-repeat;\n border-radius: var(--curvature);\n max-height: calc(var(--thumb-size, 4em) * 2);\n max-width: calc(var(--thumb-size, 4em) * 2);\n height: var(--thumb-size, 4em);\n display: flex;\n align-items: center;\n justify-content: center;\n font-weight: 400;\n color: var(--thumb-icon, #bbb);\n }\n\n .thumb.document,\n .thumb.video {\n border: 1px solid #eee;\n }\n\n .audio-player {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n background: rgba(0, 0, 0, 0.05);\n border-radius: var(--curvature);\n cursor: default;\n }\n\n .audio-play-btn {\n cursor: pointer;\n color: #666;\n display: flex;\n align-items: center;\n flex-shrink: 0;\n }\n\n .audio-play-btn:hover {\n color: #333;\n }\n\n .audio-progress-bar {\n flex: 1;\n height: 3px;\n background: #ddd;\n border-radius: 2px;\n overflow: hidden;\n min-width: 60px;\n cursor: pointer;\n }\n\n .audio-progress-fill {\n height: 100%;\n background: var(--color-primary, #2387ca);\n border-radius: 2px;\n transition: width 0.15s linear;\n }\n\n .audio-time {\n font-size: 11px;\n color: #999;\n flex-shrink: 0;\n min-width: 28px;\n text-align: right;\n }\n\n .wrapper:hover .thumb.icon {\n }\n\n .viewer {\n display: block;\n }\n\n .zoom .viewer {\n display: block;\n }\n\n .download {\n display: none;\n position: absolute;\n right: 0em;\n bottom: 0em;\n border-radius: var(--curvature);\n transform: scale(0.2) translate(3em, 3em);\n padding: 0.4em;\n }\n\n .zoom .download {\n display: block;\n background: rgba(0, 0, 0, 0.5);\n }\n\n .zoom .download:hover {\n background: rgba(0, 0, 0, 0.6);\n cursor: pointer;\n }\n `;\n }\n\n @property({ type: String })\n url: string;\n\n @property({ type: String })\n attachment: string;\n\n @property({ type: Number })\n ratio: number = 0;\n\n @property({ type: Boolean })\n preview: boolean = true;\n\n @property({ type: Boolean, attribute: false })\n zoom: boolean;\n\n @property({ type: String, attribute: true })\n contentType: string;\n\n @property({ type: Number, attribute: false })\n latitude: number;\n\n @property({ type: Number, attribute: false })\n longitude: number;\n\n // cached tile URL for location thumbnails\n @property({ type: String, attribute: false })\n private tileUrl: string = '';\n\n // audio player state\n private audio: HTMLAudioElement | null = null;\n\n @state()\n private audioPlaying = false;\n\n @state()\n private audioProgress = 0;\n\n @state()\n private audioDuration = 0;\n\n private handleAudioPlayClick(e: Event) {\n e.stopPropagation();\n\n if (!this.audio) {\n this.audio = new Audio(this.url);\n this.audio.addEventListener('timeupdate', () => {\n if (this.audio.duration) {\n this.audioProgress = this.audio.currentTime / this.audio.duration;\n this.audioDuration = this.audio.duration;\n }\n });\n this.audio.addEventListener('ended', () => {\n this.audioPlaying = false;\n this.audioProgress = 0;\n });\n this.audio.addEventListener('error', () => {\n this.audioPlaying = false;\n this.audioProgress = 0;\n });\n }\n\n if (this.audioPlaying) {\n this.audio.pause();\n this.audioPlaying = false;\n } else {\n this.audio.play().catch(() => {\n this.audioPlaying = false;\n });\n this.audioPlaying = true;\n }\n }\n\n private handleProgressClick(e: MouseEvent) {\n e.stopPropagation();\n if (!this.audio || !this.audio.duration) return;\n const bar = e.currentTarget as HTMLElement;\n const rect = bar.getBoundingClientRect();\n const pct = (e.clientX - rect.left) / rect.width;\n this.audio.currentTime = pct * this.audio.duration;\n }\n\n private formatTime(seconds: number): string {\n const s = Math.floor(seconds);\n const m = Math.floor(s / 60);\n const rem = s % 60;\n return `${m}:${rem.toString().padStart(2, '0')}`;\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n if (this.audio) {\n this.audio.pause();\n this.audio = null;\n }\n }\n\n // convert lat/lng to tile coordinates for OSM\n private latLngToTile(lat: number, lng: number, zoom: number) {\n const n = Math.pow(2, zoom);\n const x = Math.floor(((lng + 180) / 360) * n);\n const latRad = (lat * Math.PI) / 180;\n const y = Math.floor(\n ((1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2) *\n n\n );\n return { x, y, z: zoom };\n }\n\n protected updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n\n if (\n changes.has('contentType') &&\n this.contentType === ThumbnailContentType.IMAGE\n ) {\n const toObserve = this.shadowRoot.querySelector('.observe');\n if (toObserve) {\n new ResizeObserver((e, observer) => {\n if (toObserve.clientHeight > 0 && toObserve.clientWidth > 0) {\n this.ratio = toObserve.clientHeight / toObserve.clientWidth;\n this.preview =\n this.ratio === 0 || (this.ratio > 0.25 && this.ratio <= 1.5);\n observer.disconnect();\n }\n }).observe(toObserve);\n }\n }\n\n // convert our attachment to a url and label\n if (changes.has('attachment')) {\n if (this.attachment) {\n const splitIndex = this.attachment.indexOf(':');\n if (splitIndex === -1) {\n this.url = this.attachment;\n } else {\n const contentType = this.attachment.substring(0, splitIndex);\n this.url = this.attachment.substring(splitIndex + 1);\n\n if (contentType.startsWith('image')) {\n this.contentType = ThumbnailContentType.IMAGE;\n } else if (contentType.startsWith('audio')) {\n this.contentType = ThumbnailContentType.AUDIO;\n } else if (contentType.startsWith('video')) {\n this.contentType = ThumbnailContentType.VIDEO;\n } else if (contentType.startsWith('application')) {\n this.contentType = ThumbnailContentType.DOCUMENT;\n } else if (contentType.startsWith('geo')) {\n this.contentType = ThumbnailContentType.LOCATION;\n // Parse coordinates from URL which is already stripped of \"geo:\" prefix\n // Format is now just: lat,lng\n const coords = this.url.match(/^([^,]+),([^,]+)/);\n if (coords) {\n this.latitude = parseFloat(coords[1]);\n this.longitude = parseFloat(coords[2]);\n }\n } else {\n this.contentType = ThumbnailContentType.OTHER;\n }\n }\n }\n }\n\n // calculate tile URL when latitude/longitude changes\n if (changes.has('latitude') || changes.has('longitude')) {\n if (\n this.latitude !== undefined &&\n this.longitude !== undefined &&\n !isNaN(this.latitude) &&\n !isNaN(this.longitude)\n ) {\n const tile = this.latLngToTile(this.latitude, this.longitude, 13);\n this.tileUrl = `https://tile.openstreetmap.org/${tile.z}/${tile.x}/${tile.y}.png`;\n } else {\n this.tileUrl = '';\n }\n }\n }\n\n public handleThumbnailClicked() {\n if (this.contentType === ThumbnailContentType.IMAGE && this.preview) {\n window.setTimeout(() => {\n const lightbox = document.querySelector('temba-lightbox') as Lightbox;\n lightbox.showElement(this);\n }, 100);\n } else if (this.contentType === ThumbnailContentType.LOCATION) {\n // open location in openstreetmap\n const osmUrl = `https://www.openstreetmap.org/?mlat=${this.latitude}&mlon=${this.longitude}#map=15/${this.latitude}/${this.longitude}`;\n window.open(osmUrl, '_blank');\n } else if (this.contentType === ThumbnailContentType.AUDIO) {\n // audio has inline controls, no click action needed\n } else {\n window.open(this.url, '_blank');\n }\n }\n\n public handleDownload(e: Event) {\n e.stopPropagation();\n e.preventDefault();\n\n // open this.url in another tab\n window.open(this.url, '_blank');\n }\n\n public render() {\n return html`\n <div\n @click=${this.handleThumbnailClicked.bind(this)}\n class=\"${getClasses({ wrapper: true, zoom: this.zoom })}\"\n style=\"${this.contentType === ThumbnailContentType.AUDIO\n ? 'cursor: default;'\n : ''}\"\n url=${this.url}\n >\n ${this.contentType === ThumbnailContentType.IMAGE && this.preview\n ? html`<div class=\"\"><div class=\"download\" @click=${this.handleDownload.bind(\n this\n )}><temba-icon size=\"1\" style=\"color:#fff;\" name=\"download\"></temba-icon></div><img\n class=\"observe thumb ${this.contentType}\"\n src=\"${this.url}\"\n ></img></div>`\n : this.contentType === ThumbnailContentType.AUDIO\n ? html`<div class=\"audio-player\">\n <div class=\"audio-play-btn\" @click=${this.handleAudioPlayClick}>\n ${this.audioPlaying\n ? html`<svg\n viewBox=\"0 0 24 24\"\n width=\"14\"\n height=\"14\"\n fill=\"currentColor\"\n >\n <rect x=\"5\" y=\"3\" width=\"4\" height=\"18\" />\n <rect x=\"15\" y=\"3\" width=\"4\" height=\"18\" />\n </svg>`\n : html`<svg\n viewBox=\"0 0 24 24\"\n width=\"14\"\n height=\"14\"\n fill=\"currentColor\"\n >\n <polygon points=\"6,3 20,12 6,21\" />\n </svg>`}\n </div>\n <div\n class=\"audio-progress-bar\"\n @click=${this.handleProgressClick}\n >\n <div\n class=\"audio-progress-fill\"\n style=\"width: ${this.audioProgress * 100}%\"\n ></div>\n </div>\n <div class=\"audio-time\">\n ${this.audioDuration\n ? this.formatTime(\n this.audioPlaying || this.audioProgress > 0\n ? this.audio?.currentTime || 0\n : this.audioDuration\n )\n : ''}\n </div>\n </div>`\n : html`\n ${this.contentType === ThumbnailContentType.LOCATION\n ? html`<img\n style=\"height:125px;margin-bottom:-3px;border-radius:var(--curvature);\"\n src=\"${this.tileUrl}\"\n alt=\"Location preview\"\n />`\n : html`<div\n style=\"padding:1em; background:rgba(0,0,0,.05);border-radius:var(--curvature);\"\n >\n <temba-icon\n size=\"1.5\"\n name=\"${ThumbnailIcons[this.contentType]}\"\n ></temba-icon>\n </div>`}\n `}\n </div>\n `;\n }\n}\n"]}
|
|
@@ -79,20 +79,22 @@ export class CanvasMenu extends RapidElement {
|
|
|
79
79
|
}
|
|
80
80
|
firstUpdated(_changedProperties) {
|
|
81
81
|
super.firstUpdated(_changedProperties);
|
|
82
|
-
// Close menu when clicking outside
|
|
82
|
+
// Close menu when clicking outside — use mousedown instead of click
|
|
83
|
+
// to avoid being triggered by the click synthesized from a drag-and-drop
|
|
84
|
+
// (mousedown on exit + mouseup on canvas = click on common ancestor)
|
|
83
85
|
const handleClickOutside = (e) => {
|
|
84
86
|
if (this.open && !this.contains(e.target)) {
|
|
85
87
|
this.close();
|
|
86
88
|
}
|
|
87
89
|
};
|
|
88
|
-
document.addEventListener('
|
|
90
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
89
91
|
// Store cleanup function
|
|
90
92
|
this._clickOutsideHandler = handleClickOutside;
|
|
91
93
|
}
|
|
92
94
|
disconnectedCallback() {
|
|
93
95
|
super.disconnectedCallback();
|
|
94
96
|
if (this._clickOutsideHandler) {
|
|
95
|
-
document.removeEventListener('
|
|
97
|
+
document.removeEventListener('mousedown', this._clickOutsideHandler);
|
|
96
98
|
}
|
|
97
99
|
}
|
|
98
100
|
show(x, y, clickPosition, showStickyNote = 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: 'sticky' | 'action' | 'split';\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 margin-top: 0.15em;\n }\n\n .menu-item-content {\n display: flex;\n flex-direction: column;\n gap: 0.15em;\n }\n\n .menu-item-title {\n font-weight: 500;\n font-size: 1rem;\n color: var(--color-text-dark);\n }\n\n .menu-item-description {\n font-size: 0.85rem;\n color: var(--color-text);\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 @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\n const handleClickOutside = (e: MouseEvent) => {\n if (this.open && !this.contains(e.target as Node)) {\n this.close();\n }\n };\n\n document.addEventListener('click', 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('click', (this as any)._clickOutsideHandler);\n }\n }\n\n public show(\n x: number,\n y: number,\n clickPosition: { x: number; y: number },\n showStickyNote: boolean = true\n ) {\n this.x = x;\n this.y = y;\n this.clickPosition = clickPosition;\n this.showStickyNote = showStickyNote;\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: 'sticky' | 'action' | 'split') {\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('action')}\n >\n <temba-icon name=\"action\" size=\"1.25\"></temba-icon>\n <div class=\"menu-item-content\">\n <div class=\"menu-item-title\">Add Action</div>\n <div class=\"menu-item-description\">\n Send messages, update contacts\n </div>\n </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-content\">\n <div class=\"menu-item-title\">Add Split</div>\n <div class=\"menu-item-description\">Branch based on conditions</div>\n </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-content\">\n <div class=\"menu-item-title\">Add Sticky Note</div>\n <div class=\"menu-item-description\">\n Add a note to the canvas\n </div>\n </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: 'sticky' | 'action' | 'split';\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 margin-top: 0.15em;\n }\n\n .menu-item-content {\n display: flex;\n flex-direction: column;\n gap: 0.15em;\n }\n\n .menu-item-title {\n font-weight: 500;\n font-size: 1rem;\n color: var(--color-text-dark);\n }\n\n .menu-item-description {\n font-size: 0.85rem;\n color: var(--color-text);\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 @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 ) {\n this.x = x;\n this.y = y;\n this.clickPosition = clickPosition;\n this.showStickyNote = showStickyNote;\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: 'sticky' | 'action' | 'split') {\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('action')}\n >\n <temba-icon name=\"action\" size=\"1.25\"></temba-icon>\n <div class=\"menu-item-content\">\n <div class=\"menu-item-title\">Add Action</div>\n <div class=\"menu-item-description\">\n Send messages, update contacts\n </div>\n </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-content\">\n <div class=\"menu-item-title\">Add Split</div>\n <div class=\"menu-item-description\">Branch based on conditions</div>\n </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-content\">\n <div class=\"menu-item-title\">Add Sticky Note</div>\n <div class=\"menu-item-description\">\n Add a note to the canvas\n </div>\n </div>\n </div>\n `\n : ''}\n </div>\n `;\n }\n}\n"]}
|
|
@@ -111,6 +111,12 @@ export class CanvasNode extends RapidElement {
|
|
|
111
111
|
pointer-events: none !important;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
/* Issue indicators - hatched red title bar */
|
|
115
|
+
.action-content.has-issues .cn-title,
|
|
116
|
+
.node.has-issues > .router .cn-title {
|
|
117
|
+
background: repeating-linear-gradient(120deg, tomato, tomato 6px, #ff7056 0, #ff7056 18px) !important;
|
|
118
|
+
}
|
|
119
|
+
|
|
114
120
|
.action.sortable {
|
|
115
121
|
display: flex;
|
|
116
122
|
align-items: stretch;
|
|
@@ -286,11 +292,7 @@ export class CanvasNode extends RapidElement {
|
|
|
286
292
|
position: relative;
|
|
287
293
|
box-shadow: 0 2px 2px rgba(0, 0, 0, .1);
|
|
288
294
|
cursor: pointer;
|
|
289
|
-
pointer-events:
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
.exit.jtk-connected {
|
|
293
|
-
background: var(--color-connectors, #e6e6e6);
|
|
295
|
+
pointer-events: all;
|
|
294
296
|
}
|
|
295
297
|
|
|
296
298
|
.exit.connected {
|
|
@@ -302,11 +304,14 @@ export class CanvasNode extends RapidElement {
|
|
|
302
304
|
background-color: var(--color-connectors, #e6e6e6);
|
|
303
305
|
}
|
|
304
306
|
|
|
305
|
-
.exit.
|
|
306
|
-
background-color: #fff;
|
|
307
|
+
.exit.read-only, .exit.read-only:hover {
|
|
307
308
|
pointer-events: none !important;
|
|
308
309
|
cursor: default;
|
|
309
310
|
}
|
|
311
|
+
|
|
312
|
+
.exit.connected.read-only, .exit.connected.read-only:hover {
|
|
313
|
+
background-color: #fff;
|
|
314
|
+
}
|
|
310
315
|
|
|
311
316
|
.exit.removing, .exit.removing:hover {
|
|
312
317
|
background-color: var(--color-error);
|
|
@@ -473,10 +478,14 @@ export class CanvasNode extends RapidElement {
|
|
|
473
478
|
// make our initial connections
|
|
474
479
|
// We use setTimeout to allow for DOM updates to complete before querying for exits
|
|
475
480
|
this.connectionTimeout = window.setTimeout(() => {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
481
|
+
var _b;
|
|
482
|
+
// Terminal nodes have no visible exits
|
|
483
|
+
if (((_b = this.ui) === null || _b === void 0 ? void 0 : _b.type) !== 'terminal') {
|
|
484
|
+
for (const exit of this.node.exits) {
|
|
485
|
+
this.plumber.makeSource(exit.uuid);
|
|
486
|
+
if (exit.destination_uuid) {
|
|
487
|
+
this.plumber.connectIds(this.node.uuid, exit.uuid, exit.destination_uuid);
|
|
488
|
+
}
|
|
480
489
|
}
|
|
481
490
|
}
|
|
482
491
|
// Note: revalidation is handled by plumber's processPendingConnections which calls repaintEverything
|
|
@@ -772,6 +781,10 @@ export class CanvasNode extends RapidElement {
|
|
|
772
781
|
}
|
|
773
782
|
this.requestUpdate();
|
|
774
783
|
}
|
|
784
|
+
getTopCenter(el) {
|
|
785
|
+
const rect = el.getBoundingClientRect();
|
|
786
|
+
return { x: rect.left + rect.width / 2, y: rect.top };
|
|
787
|
+
}
|
|
775
788
|
handleActionMouseDown(event, action) {
|
|
776
789
|
// Don't handle clicks on the remove button, drag handle, or when action is in removing state
|
|
777
790
|
const target = event.target;
|
|
@@ -813,10 +826,17 @@ export class CanvasNode extends RapidElement {
|
|
|
813
826
|
// Only fire the action edit event if we haven't dragged beyond the threshold
|
|
814
827
|
// AND either there's no Editor parent (test case) or the Editor didn't drag the node
|
|
815
828
|
if (distance <= DRAG_THRESHOLD && (!editor || !editorWasDragging)) {
|
|
829
|
+
// Use top-center of the action element as the dialog origin
|
|
830
|
+
const actionEl = event.currentTarget;
|
|
831
|
+
const origin = actionEl
|
|
832
|
+
? this.getTopCenter(actionEl)
|
|
833
|
+
: { x: event.clientX, y: event.clientY };
|
|
816
834
|
// Fire event to request action editing
|
|
817
835
|
this.fireCustomEvent(CustomEventType.ActionEditRequested, {
|
|
818
836
|
action,
|
|
819
|
-
nodeUuid: this.node.uuid
|
|
837
|
+
nodeUuid: this.node.uuid,
|
|
838
|
+
originX: origin.x,
|
|
839
|
+
originY: origin.y
|
|
820
840
|
});
|
|
821
841
|
}
|
|
822
842
|
}
|
|
@@ -915,18 +935,24 @@ export class CanvasNode extends RapidElement {
|
|
|
915
935
|
// Using literal 5 instead of DRAG_THRESHOLD since it's not imported
|
|
916
936
|
// Fire event to request node editing if the node has a router
|
|
917
937
|
if (this.node.router) {
|
|
938
|
+
// Use top-center of the node as the dialog origin
|
|
939
|
+
const origin = this.getTopCenter(this);
|
|
918
940
|
// If router node has exactly one action, open the action editor directly
|
|
919
941
|
if (this.node.actions && this.node.actions.length === 1) {
|
|
920
942
|
this.fireCustomEvent(CustomEventType.ActionEditRequested, {
|
|
921
943
|
action: this.node.actions[0],
|
|
922
|
-
nodeUuid: this.node.uuid
|
|
944
|
+
nodeUuid: this.node.uuid,
|
|
945
|
+
originX: origin.x,
|
|
946
|
+
originY: origin.y
|
|
923
947
|
});
|
|
924
948
|
}
|
|
925
949
|
else {
|
|
926
950
|
// Otherwise open the node editor as before
|
|
927
951
|
this.fireCustomEvent(CustomEventType.NodeEditRequested, {
|
|
928
952
|
node: this.node,
|
|
929
|
-
nodeUI: this.ui
|
|
953
|
+
nodeUI: this.ui,
|
|
954
|
+
originX: origin.x,
|
|
955
|
+
originY: origin.y
|
|
930
956
|
});
|
|
931
957
|
}
|
|
932
958
|
}
|
|
@@ -1062,17 +1088,15 @@ export class CanvasNode extends RapidElement {
|
|
|
1062
1088
|
this.requestUpdate();
|
|
1063
1089
|
}
|
|
1064
1090
|
renderTitle(config, action, index, isRemoving = false) {
|
|
1065
|
-
var _b, _c, _d, _e;
|
|
1091
|
+
var _b, _c, _d, _e, _f;
|
|
1066
1092
|
const color = config.group
|
|
1067
1093
|
? (_b = ACTION_GROUP_METADATA[config.group]) === null || _b === void 0 ? void 0 : _b.color
|
|
1068
1094
|
: '#aaaaaa';
|
|
1095
|
+
const isTerminal = ((_c = this.ui) === null || _c === void 0 ? void 0 : _c.type) === 'terminal';
|
|
1069
1096
|
return html `<div class="cn-title" style="background:${color}">
|
|
1070
|
-
${
|
|
1071
|
-
? html `<
|
|
1072
|
-
|
|
1073
|
-
name="sort"
|
|
1074
|
-
></temba-icon>`
|
|
1075
|
-
: ((_e = (_d = this.node) === null || _d === void 0 ? void 0 : _d.actions) === null || _e === void 0 ? void 0 : _e.length) > 1
|
|
1097
|
+
${isTerminal
|
|
1098
|
+
? html `<div class="title-spacer"></div>`
|
|
1099
|
+
: ((_d = this.ui) === null || _d === void 0 ? void 0 : _d.type) === 'execute_actions' || ((_f = (_e = this.node) === null || _e === void 0 ? void 0 : _e.actions) === null || _f === void 0 ? void 0 : _f.length) > 1
|
|
1076
1100
|
? html `<temba-icon
|
|
1077
1101
|
class="drag-handle ${this.isReadOnly() ? 'read-only-hidden' : ''}"
|
|
1078
1102
|
name="sort"
|
|
@@ -1081,7 +1105,9 @@ export class CanvasNode extends RapidElement {
|
|
|
1081
1105
|
|
|
1082
1106
|
<div class="name">${isRemoving ? 'Remove?' : config.name}</div>
|
|
1083
1107
|
<div
|
|
1084
|
-
class="remove-button ${this.isReadOnly()
|
|
1108
|
+
class="remove-button ${isTerminal || this.isReadOnly()
|
|
1109
|
+
? 'read-only-hidden'
|
|
1110
|
+
: ''}"
|
|
1085
1111
|
@click=${(e) => this.handleActionRemoveClick(e, action, index)}
|
|
1086
1112
|
title="Remove action"
|
|
1087
1113
|
>
|
|
@@ -1166,7 +1192,7 @@ export class CanvasNode extends RapidElement {
|
|
|
1166
1192
|
return localizedAction;
|
|
1167
1193
|
}
|
|
1168
1194
|
renderAction(node, action, index) {
|
|
1169
|
-
var _b, _c, _d;
|
|
1195
|
+
var _b, _c, _d, _e;
|
|
1170
1196
|
const config = ACTION_CONFIG[action.type];
|
|
1171
1197
|
const isRemoving = this.actionRemovingState.has(action.uuid);
|
|
1172
1198
|
const isLocalizable = (config === null || config === void 0 ? void 0 : config.localizable) && config.localizable.length > 0;
|
|
@@ -1177,6 +1203,7 @@ export class CanvasNode extends RapidElement {
|
|
|
1177
1203
|
// Get the localized action if translating
|
|
1178
1204
|
const displayAction = this.getLocalizedAction(action);
|
|
1179
1205
|
if (config) {
|
|
1206
|
+
const hasIssues = (_e = this.issuesByAction) === null || _e === void 0 ? void 0 : _e.has(action.uuid);
|
|
1180
1207
|
const classes = [
|
|
1181
1208
|
'action',
|
|
1182
1209
|
'sortable',
|
|
@@ -1190,7 +1217,7 @@ export class CanvasNode extends RapidElement {
|
|
|
1190
1217
|
.join(' ');
|
|
1191
1218
|
return html `<div class="${classes}" id="action-${index}">
|
|
1192
1219
|
<div
|
|
1193
|
-
class="action-content"
|
|
1220
|
+
class="action-content ${hasIssues ? 'has-issues' : ''}"
|
|
1194
1221
|
@mousedown=${(e) => !isDisabled && this.handleActionMouseDown(e, action)}
|
|
1195
1222
|
@mouseup=${(e) => !isDisabled && this.handleActionMouseUp(e, action)}
|
|
1196
1223
|
style="cursor: ${isDisabled ? 'not-allowed' : 'pointer'}"
|
|
@@ -1321,7 +1348,7 @@ export class CanvasNode extends RapidElement {
|
|
|
1321
1348
|
return this.viewingRevision || this.isTranslating;
|
|
1322
1349
|
}
|
|
1323
1350
|
render() {
|
|
1324
|
-
var _b;
|
|
1351
|
+
var _b, _c, _d, _e;
|
|
1325
1352
|
if (!this.node || !this.ui) {
|
|
1326
1353
|
return html `<div class="node">Loading...</div>`;
|
|
1327
1354
|
}
|
|
@@ -1333,13 +1360,17 @@ export class CanvasNode extends RapidElement {
|
|
|
1333
1360
|
!this.includeCategoriesInTranslation;
|
|
1334
1361
|
// Get active contact count for this node
|
|
1335
1362
|
const activeCount = (((_b = this.activity) === null || _b === void 0 ? void 0 : _b.nodes) && this.activity.nodes[this.node.uuid]) || 0;
|
|
1363
|
+
// Check for node-level issues or action-level issues on any action in this node
|
|
1364
|
+
const nodeHasIssues = ((_c = this.issuesByNode) === null || _c === void 0 ? void 0 : _c.has(this.node.uuid)) ||
|
|
1365
|
+
((_d = this.node.actions) === null || _d === void 0 ? void 0 : _d.some((a) => { var _b; return (_b = this.issuesByAction) === null || _b === void 0 ? void 0 : _b.has(a.uuid); }));
|
|
1336
1366
|
return html `
|
|
1337
1367
|
<div
|
|
1338
1368
|
id="${this.node.uuid}"
|
|
1339
1369
|
class=${getClasses({
|
|
1340
1370
|
node: true,
|
|
1341
1371
|
'execute-actions': this.ui.type === 'execute_actions',
|
|
1342
|
-
'non-localizable': isNodeDisabled
|
|
1372
|
+
'non-localizable': isNodeDisabled,
|
|
1373
|
+
'has-issues': nodeHasIssues
|
|
1343
1374
|
})}
|
|
1344
1375
|
style="left:${this.ui.position.left}px;top:${this.ui.position.top}px"
|
|
1345
1376
|
>
|
|
@@ -1348,7 +1379,9 @@ export class CanvasNode extends RapidElement {
|
|
|
1348
1379
|
${activeCount.toLocaleString()}
|
|
1349
1380
|
</div>`
|
|
1350
1381
|
: ''}
|
|
1351
|
-
${nodeConfig &&
|
|
1382
|
+
${nodeConfig &&
|
|
1383
|
+
nodeConfig.type !== 'execute_actions' &&
|
|
1384
|
+
nodeConfig.type !== 'terminal'
|
|
1352
1385
|
? html `<div class="router" style="position: relative;">
|
|
1353
1386
|
<div
|
|
1354
1387
|
@mousedown=${(e) => this.handleNodeMouseDown(e)}
|
|
@@ -1361,7 +1394,7 @@ export class CanvasNode extends RapidElement {
|
|
|
1361
1394
|
: null}
|
|
1362
1395
|
</div>
|
|
1363
1396
|
</div>`
|
|
1364
|
-
: this.node.actions.length > 0
|
|
1397
|
+
: ((_e = this.node.actions) === null || _e === void 0 ? void 0 : _e.length) > 0
|
|
1365
1398
|
? this.ui.type === 'execute_actions'
|
|
1366
1399
|
? html `<temba-sortable-list
|
|
1367
1400
|
dragHandle="drag-handle"
|
|
@@ -1389,7 +1422,9 @@ export class CanvasNode extends RapidElement {
|
|
|
1389
1422
|
${this.renderRouter(this.node.router, this.ui)}
|
|
1390
1423
|
${this.renderCategories(this.node)}
|
|
1391
1424
|
</div>`
|
|
1392
|
-
:
|
|
1425
|
+
: this.ui.type === 'terminal'
|
|
1426
|
+
? ''
|
|
1427
|
+
: html `<div class="action-exits">
|
|
1393
1428
|
${repeat(this.node.exits, (exit) => exit.uuid, (exit) => this.renderExit(exit))}
|
|
1394
1429
|
</div>`}
|
|
1395
1430
|
${this.ui.type === 'execute_actions' && !this.isReadOnly()
|
|
@@ -1432,4 +1467,10 @@ __decorate([
|
|
|
1432
1467
|
__decorate([
|
|
1433
1468
|
fromStore(zustand, (state) => state.getCurrentActivity())
|
|
1434
1469
|
], CanvasNode.prototype, "activity", void 0);
|
|
1470
|
+
__decorate([
|
|
1471
|
+
fromStore(zustand, (state) => state.issuesByNode)
|
|
1472
|
+
], CanvasNode.prototype, "issuesByNode", void 0);
|
|
1473
|
+
__decorate([
|
|
1474
|
+
fromStore(zustand, (state) => state.issuesByAction)
|
|
1475
|
+
], CanvasNode.prototype, "issuesByAction", void 0);
|
|
1435
1476
|
//# sourceMappingURL=CanvasNode.js.map
|