@nyaruka/temba-components 0.156.2 → 0.156.4
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 +21 -0
- package/dist/temba-components.js +560 -391
- package/dist/temba-components.js.map +1 -1
- package/package.json +1 -1
- package/src/display/Chat.ts +19 -17
- package/src/events/eventRenderers.ts +6 -1
- package/src/flow/CanvasNode.ts +69 -2
- package/src/flow/Editor.ts +260 -510
- package/src/flow/EditorToolbar.ts +566 -0
- package/src/flow/FlowSearch.ts +42 -10
- package/src/flow/MessageTable.ts +332 -122
- package/src/flow/NodeEditor.ts +29 -4
- package/src/flow/StickyNote.ts +29 -5
- package/src/flow/actions/say_msg.ts +1 -0
- package/src/flow/actions/send_broadcast.ts +48 -0
- package/src/flow/actions/send_email.ts +42 -1
- package/src/flow/actions/send_msg.ts +1 -0
- package/src/flow/actions/set_contact_language.ts +4 -13
- package/src/flow/actions/set_run_result.ts +1 -0
- package/src/flow/nodes/shared-rules.ts +1 -0
- package/src/flow/nodes/shared.ts +1 -0
- package/src/flow/nodes/wait_for_audio.ts +1 -0
- package/src/flow/utils.ts +11 -0
- package/src/simulator/Simulator.ts +17 -12
- package/temba-modules.ts +2 -0
- package/web-test-runner.config.mjs +2 -0
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
import { css, html, TemplateResult } from 'lit';
|
|
2
|
+
import { property, state } from 'lit/decorators.js';
|
|
3
|
+
import { RapidElement } from '../RapidElement';
|
|
4
|
+
import { CustomEventType } from '../interfaces';
|
|
5
|
+
import { Icon } from '../Icons';
|
|
6
|
+
|
|
7
|
+
export interface LanguageOption {
|
|
8
|
+
name: string;
|
|
9
|
+
value: string;
|
|
10
|
+
percent?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const PRIMARY_LANGUAGE_OPTION_VALUE = '__primary_language__';
|
|
14
|
+
|
|
15
|
+
export class EditorToolbar extends RapidElement {
|
|
16
|
+
static get styles() {
|
|
17
|
+
return css`
|
|
18
|
+
:host {
|
|
19
|
+
display: block;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.editor-toolbar {
|
|
23
|
+
--toolbar-control-height: 28px;
|
|
24
|
+
--toolbar-translation-control-height: 28px;
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
padding: 6px 12px;
|
|
28
|
+
background: #fff;
|
|
29
|
+
border-bottom: 1px solid #e8e8e8;
|
|
30
|
+
flex-shrink: 0;
|
|
31
|
+
gap: 8px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.toolbar-left {
|
|
35
|
+
display: flex;
|
|
36
|
+
align-items: center;
|
|
37
|
+
gap: 2px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.toolbar-right {
|
|
41
|
+
display: flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
gap: 2px;
|
|
44
|
+
margin-left: auto;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.toolbar-btn {
|
|
48
|
+
width: var(--toolbar-control-height);
|
|
49
|
+
height: var(--toolbar-control-height);
|
|
50
|
+
border: none;
|
|
51
|
+
background: transparent;
|
|
52
|
+
border-radius: var(--curvature);
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
display: flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
justify-content: center;
|
|
57
|
+
padding: 0;
|
|
58
|
+
color: #888;
|
|
59
|
+
font-size: 16px;
|
|
60
|
+
line-height: 1;
|
|
61
|
+
outline: none;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.toolbar-btn:focus {
|
|
65
|
+
outline: none;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.toolbar-btn:focus-visible {
|
|
69
|
+
outline: 2px solid #0064c8;
|
|
70
|
+
outline-offset: 2px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.toolbar-btn:hover {
|
|
74
|
+
background: rgba(0, 0, 0, 0.06);
|
|
75
|
+
color: #555;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.toolbar-btn:disabled {
|
|
79
|
+
opacity: 0.3;
|
|
80
|
+
cursor: default;
|
|
81
|
+
background: transparent;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.toolbar-btn.active {
|
|
85
|
+
background: rgba(0, 100, 200, 0.1);
|
|
86
|
+
color: #0064c8;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.toolbar-btn.active:hover {
|
|
90
|
+
background: rgba(0, 100, 200, 0.15);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.toolbar-tip {
|
|
94
|
+
display: flex;
|
|
95
|
+
align-items: center;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.toolbar-divider {
|
|
99
|
+
width: 1px;
|
|
100
|
+
height: 16px;
|
|
101
|
+
background: #e0e0e0;
|
|
102
|
+
margin: 0 4px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.toolbar-group {
|
|
106
|
+
display: flex;
|
|
107
|
+
align-items: center;
|
|
108
|
+
gap: 4px;
|
|
109
|
+
height: var(--toolbar-control-height);
|
|
110
|
+
box-sizing: border-box;
|
|
111
|
+
padding: 0 3px;
|
|
112
|
+
border: 1px solid #d7dce2;
|
|
113
|
+
border-radius: calc(var(--curvature) + 2px);
|
|
114
|
+
background: #f7f9fb;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.toolbar-group-divider {
|
|
118
|
+
width: 1px;
|
|
119
|
+
height: 18px;
|
|
120
|
+
background: #d7dce2;
|
|
121
|
+
margin: 0 2px;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.toolbar-language {
|
|
125
|
+
position: relative;
|
|
126
|
+
display: flex;
|
|
127
|
+
align-items: center;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.toolbar-language-group {
|
|
131
|
+
display: flex;
|
|
132
|
+
align-items: center;
|
|
133
|
+
gap: 6px;
|
|
134
|
+
margin-left: 2px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.toolbar-zoom-group {
|
|
138
|
+
gap: 2px;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.language-pill {
|
|
142
|
+
display: flex;
|
|
143
|
+
align-items: center;
|
|
144
|
+
gap: 6px;
|
|
145
|
+
background: #e9eef4;
|
|
146
|
+
color: #0064c8;
|
|
147
|
+
height: var(--toolbar-translation-control-height);
|
|
148
|
+
padding: 0 8px;
|
|
149
|
+
border-radius: var(--curvature);
|
|
150
|
+
box-sizing: border-box;
|
|
151
|
+
font-size: 13px;
|
|
152
|
+
font-weight: 400;
|
|
153
|
+
white-space: nowrap;
|
|
154
|
+
cursor: pointer;
|
|
155
|
+
--icon-color: #0064c8;
|
|
156
|
+
--icon-size: 16px;
|
|
157
|
+
border: none;
|
|
158
|
+
outline: none;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.language-pill:hover {
|
|
162
|
+
filter: brightness(1.04);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.language-pill.primary {
|
|
166
|
+
background: #fff;
|
|
167
|
+
border: 1px solid #d7dce2;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.language-pill.complete {
|
|
171
|
+
background: #d4f5e0;
|
|
172
|
+
color: #1a7f37;
|
|
173
|
+
--icon-color: #1a7f37;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.language-pill-caret {
|
|
177
|
+
margin-left: 1px;
|
|
178
|
+
--icon-color: currentColor;
|
|
179
|
+
--icon-size: 12px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.language-percent {
|
|
183
|
+
display: inline-block;
|
|
184
|
+
font-size: 12px;
|
|
185
|
+
font-weight: 700;
|
|
186
|
+
line-height: 1;
|
|
187
|
+
color: #0064c8;
|
|
188
|
+
white-space: nowrap;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.language-pill.complete .language-percent {
|
|
192
|
+
color: #1a7f37;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.toolbar-zoom-level {
|
|
196
|
+
font-size: 12px;
|
|
197
|
+
min-width: 40px;
|
|
198
|
+
text-align: center;
|
|
199
|
+
color: #555;
|
|
200
|
+
font-weight: 500;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.toolbar-translation {
|
|
204
|
+
display: flex;
|
|
205
|
+
align-items: center;
|
|
206
|
+
gap: 4px;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.toolbar-btn.language-tool {
|
|
210
|
+
width: var(--toolbar-translation-control-height);
|
|
211
|
+
height: var(--toolbar-translation-control-height);
|
|
212
|
+
}
|
|
213
|
+
`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@property({ type: Boolean, attribute: 'message-view' })
|
|
217
|
+
messageView = false;
|
|
218
|
+
|
|
219
|
+
@property({ type: Number })
|
|
220
|
+
zoom = 1.0;
|
|
221
|
+
|
|
222
|
+
@property({ type: Boolean, attribute: 'zoom-initialized' })
|
|
223
|
+
zoomInitialized = false;
|
|
224
|
+
|
|
225
|
+
@property({ type: Boolean, attribute: 'zoom-fitted' })
|
|
226
|
+
zoomFitted = false;
|
|
227
|
+
|
|
228
|
+
@property({ type: Boolean, attribute: 'revisions-active' })
|
|
229
|
+
revisionsActive = false;
|
|
230
|
+
|
|
231
|
+
@property({ type: Boolean, attribute: 'is-saving' })
|
|
232
|
+
isSaving = false;
|
|
233
|
+
|
|
234
|
+
@property({ type: Boolean, attribute: 'search-disabled' })
|
|
235
|
+
searchDisabled = false;
|
|
236
|
+
|
|
237
|
+
@property({ type: Array })
|
|
238
|
+
languageOptions: LanguageOption[] = [];
|
|
239
|
+
|
|
240
|
+
@property({ type: String, attribute: 'current-language-name' })
|
|
241
|
+
currentLanguageName = '';
|
|
242
|
+
|
|
243
|
+
@property({ type: Boolean, attribute: 'is-base-language' })
|
|
244
|
+
isBaseLanguage = true;
|
|
245
|
+
|
|
246
|
+
@property({ type: Number, attribute: 'language-percent' })
|
|
247
|
+
languagePercent = 0;
|
|
248
|
+
|
|
249
|
+
@property({ type: Boolean, attribute: 'show-localization-tools' })
|
|
250
|
+
showLocalizationTools = false;
|
|
251
|
+
|
|
252
|
+
@state()
|
|
253
|
+
private showLanguageOptions = false;
|
|
254
|
+
|
|
255
|
+
private fireToolbarAction(action: string, detail: any = {}): void {
|
|
256
|
+
this.fireCustomEvent(CustomEventType.ButtonClicked, { action, ...detail });
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
private handleLanguageIconClick(): void {
|
|
260
|
+
if (this.showLanguageOptions) {
|
|
261
|
+
this.showLanguageOptions = false;
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
this.showLanguageOptions = true;
|
|
265
|
+
requestAnimationFrame(() => {
|
|
266
|
+
const close = () => {
|
|
267
|
+
this.showLanguageOptions = false;
|
|
268
|
+
document.removeEventListener('click', close);
|
|
269
|
+
};
|
|
270
|
+
document.addEventListener('click', close, { once: true });
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
private handleLanguageOptionSelected(event: CustomEvent): void {
|
|
275
|
+
if (!this.showLanguageOptions) return;
|
|
276
|
+
const selected = event.detail?.selected;
|
|
277
|
+
if (selected?.value === PRIMARY_LANGUAGE_OPTION_VALUE) {
|
|
278
|
+
this.fireToolbarAction('language-change', { isPrimary: true });
|
|
279
|
+
} else if (selected?.value) {
|
|
280
|
+
this.fireToolbarAction('language-change', {
|
|
281
|
+
languageCode: selected.value
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
this.showLanguageOptions = false;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private isMacPlatform(): boolean {
|
|
288
|
+
return (
|
|
289
|
+
typeof navigator !== 'undefined' &&
|
|
290
|
+
/Mac|iPod|iPhone|iPad/.test(navigator.platform)
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private getSearchShortcutLabel(): string {
|
|
295
|
+
return this.isMacPlatform() ? '⌘F' : 'Ctrl+F';
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
private renderTip(
|
|
299
|
+
text: string | TemplateResult,
|
|
300
|
+
content: TemplateResult
|
|
301
|
+
): TemplateResult {
|
|
302
|
+
return html`
|
|
303
|
+
<temba-tip
|
|
304
|
+
class="toolbar-tip"
|
|
305
|
+
.text=${typeof text === 'string' ? text : ''}
|
|
306
|
+
.content=${typeof text === 'string' ? null : text}
|
|
307
|
+
position="top"
|
|
308
|
+
>
|
|
309
|
+
${content}
|
|
310
|
+
</temba-tip>
|
|
311
|
+
`;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private renderShortcutLabel(
|
|
315
|
+
label: string,
|
|
316
|
+
shortcut: string
|
|
317
|
+
): TemplateResult {
|
|
318
|
+
return html`<span style="display:inline-flex; align-items:center; gap:8px;">
|
|
319
|
+
<span>${label}</span>
|
|
320
|
+
<kbd>${shortcut}</kbd>
|
|
321
|
+
</span>`;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private renderLanguageOption(
|
|
325
|
+
option: LanguageOption,
|
|
326
|
+
selected: boolean
|
|
327
|
+
): TemplateResult {
|
|
328
|
+
if (option.value === PRIMARY_LANGUAGE_OPTION_VALUE) {
|
|
329
|
+
const primaryBackground = selected ? '#e1e8ef' : '#edf1f5';
|
|
330
|
+
return html`
|
|
331
|
+
<div
|
|
332
|
+
style="display:flex; align-items:center; justify-content:space-between; gap:8px; background:${primaryBackground}; color:#2f3f52; border-radius:4px; padding:6px 10px;"
|
|
333
|
+
>
|
|
334
|
+
<span>${option.name}</span>
|
|
335
|
+
<span
|
|
336
|
+
style="display:inline-flex; align-items:center; border-radius:999px; background:rgba(47, 63, 82, 0.12); color:#2f3f52; font-size:10px; font-weight:700; line-height:1; padding:3px 7px;"
|
|
337
|
+
>Original</span
|
|
338
|
+
>
|
|
339
|
+
</div>
|
|
340
|
+
`;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const isComplete = option.percent === 100;
|
|
344
|
+
const optionBg = isComplete ? '#d4f5e0' : '';
|
|
345
|
+
const optionHoverBg = isComplete ? '#c0edce' : '';
|
|
346
|
+
const optionRadius = isComplete ? 'border-radius:4px;' : '';
|
|
347
|
+
const percentColor = isComplete ? 'color:#1a7f37;' : 'color:#5f6b7a;';
|
|
348
|
+
|
|
349
|
+
return html`
|
|
350
|
+
<div
|
|
351
|
+
style="display:flex; align-items:center; justify-content:space-between; gap:8px; padding:6px 10px; ${optionBg ? `background:${optionBg};` : ''} ${optionRadius}"
|
|
352
|
+
@mouseenter=${isComplete
|
|
353
|
+
? (e: MouseEvent) => {
|
|
354
|
+
(e.currentTarget as HTMLElement).style.background = optionHoverBg;
|
|
355
|
+
}
|
|
356
|
+
: null}
|
|
357
|
+
@mouseleave=${isComplete
|
|
358
|
+
? (e: MouseEvent) => {
|
|
359
|
+
(e.currentTarget as HTMLElement).style.background = optionBg;
|
|
360
|
+
}
|
|
361
|
+
: null}
|
|
362
|
+
>
|
|
363
|
+
<span style="${isComplete ? 'color:#1a7f37;' : ''}"
|
|
364
|
+
>${option.name}</span
|
|
365
|
+
>
|
|
366
|
+
<span style="font-size:11px; font-weight:600; ${percentColor}"
|
|
367
|
+
>${option.percent ?? 0}%</span
|
|
368
|
+
>
|
|
369
|
+
</div>
|
|
370
|
+
`;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
public render(): TemplateResult {
|
|
374
|
+
const showLanguageControls = this.languageOptions.length > 1;
|
|
375
|
+
const searchTargetLabel = this.messageView
|
|
376
|
+
? 'Search table'
|
|
377
|
+
: 'Search flow';
|
|
378
|
+
|
|
379
|
+
return html`
|
|
380
|
+
<div class="editor-toolbar">
|
|
381
|
+
<div class="toolbar-left">
|
|
382
|
+
${this.renderTip(
|
|
383
|
+
'Flow View',
|
|
384
|
+
html`
|
|
385
|
+
<button
|
|
386
|
+
class="toolbar-btn ${!this.messageView ? 'active' : ''}"
|
|
387
|
+
@click=${() => this.fireToolbarAction('view-change', { view: 'flow' })}
|
|
388
|
+
aria-label="Flow View"
|
|
389
|
+
>
|
|
390
|
+
<temba-icon name="flow" size="1"></temba-icon>
|
|
391
|
+
</button>
|
|
392
|
+
`
|
|
393
|
+
)}
|
|
394
|
+
${this.renderTip(
|
|
395
|
+
'Table View',
|
|
396
|
+
html`
|
|
397
|
+
<button
|
|
398
|
+
class="toolbar-btn ${this.messageView ? 'active' : ''}"
|
|
399
|
+
@click=${() => this.fireToolbarAction('view-change', { view: 'table' })}
|
|
400
|
+
aria-label="Table View"
|
|
401
|
+
>
|
|
402
|
+
<temba-icon name=${Icon.quick_replies} size="1"></temba-icon>
|
|
403
|
+
</button>
|
|
404
|
+
`
|
|
405
|
+
)}
|
|
406
|
+
${showLanguageControls
|
|
407
|
+
? html`
|
|
408
|
+
<div class="toolbar-divider"></div>
|
|
409
|
+
<div class="toolbar-language-group">
|
|
410
|
+
<div class="toolbar-language">
|
|
411
|
+
${this.renderTip(
|
|
412
|
+
'Change language',
|
|
413
|
+
html`
|
|
414
|
+
<button
|
|
415
|
+
class="language-pill ${this.isBaseLanguage ? 'primary' : this.languagePercent === 100 ? 'complete' : ''}"
|
|
416
|
+
id="language-btn"
|
|
417
|
+
@click=${this.handleLanguageIconClick}
|
|
418
|
+
aria-label="Change language"
|
|
419
|
+
>
|
|
420
|
+
<temba-icon name=${Icon.language}></temba-icon>
|
|
421
|
+
<span>${this.currentLanguageName}</span>
|
|
422
|
+
${!this.isBaseLanguage
|
|
423
|
+
? html`<span class="language-percent"
|
|
424
|
+
>${this.languagePercent}%</span
|
|
425
|
+
>`
|
|
426
|
+
: ''}
|
|
427
|
+
<temba-icon
|
|
428
|
+
class="language-pill-caret"
|
|
429
|
+
name=${this.showLanguageOptions
|
|
430
|
+
? Icon.arrow_up
|
|
431
|
+
: Icon.arrow_down}
|
|
432
|
+
></temba-icon>
|
|
433
|
+
</button>
|
|
434
|
+
`
|
|
435
|
+
)}
|
|
436
|
+
<temba-options
|
|
437
|
+
.anchorTo=${this.shadowRoot?.querySelector('#language-btn') as HTMLElement}
|
|
438
|
+
.options=${this.languageOptions}
|
|
439
|
+
.renderOption=${this.renderLanguageOption}
|
|
440
|
+
?visible=${this.showLanguageOptions}
|
|
441
|
+
@temba-selection=${this.handleLanguageOptionSelected}
|
|
442
|
+
style="--temba-options-option-margin:4px; --temba-options-option-padding:0; --temba-options-option-radius:4px;"
|
|
443
|
+
min-width="230"
|
|
444
|
+
></temba-options>
|
|
445
|
+
</div>
|
|
446
|
+
${this.showLocalizationTools
|
|
447
|
+
? this.renderTranslationTools()
|
|
448
|
+
: ''}
|
|
449
|
+
</div>
|
|
450
|
+
`
|
|
451
|
+
: ''}
|
|
452
|
+
</div>
|
|
453
|
+
<div class="toolbar-right">
|
|
454
|
+
${!this.messageView
|
|
455
|
+
? html`
|
|
456
|
+
${this.renderTip(
|
|
457
|
+
'Zoom to fit',
|
|
458
|
+
html`
|
|
459
|
+
<button
|
|
460
|
+
class="toolbar-btn"
|
|
461
|
+
@click=${() => this.fireToolbarAction('zoom-to-fit')}
|
|
462
|
+
?disabled=${!this.zoomInitialized || this.zoomFitted}
|
|
463
|
+
aria-label="Zoom to fit"
|
|
464
|
+
>
|
|
465
|
+
<temba-icon
|
|
466
|
+
name=${Icon.zoom_fit}
|
|
467
|
+
size="1"
|
|
468
|
+
></temba-icon>
|
|
469
|
+
</button>
|
|
470
|
+
`
|
|
471
|
+
)}
|
|
472
|
+
<div class="toolbar-divider"></div>
|
|
473
|
+
${this.renderTip(
|
|
474
|
+
'Zoom out',
|
|
475
|
+
html`
|
|
476
|
+
<button
|
|
477
|
+
class="toolbar-btn"
|
|
478
|
+
@click=${() => this.fireToolbarAction('zoom-out')}
|
|
479
|
+
?disabled=${!this.zoomInitialized || this.zoom <= 0.3}
|
|
480
|
+
aria-label="Zoom out"
|
|
481
|
+
>
|
|
482
|
+
−
|
|
483
|
+
</button>
|
|
484
|
+
`
|
|
485
|
+
)}
|
|
486
|
+
<span class="toolbar-zoom-level"
|
|
487
|
+
>${this.zoomInitialized
|
|
488
|
+
? `${Math.round(this.zoom * 100)}%`
|
|
489
|
+
: ''}</span
|
|
490
|
+
>
|
|
491
|
+
${this.renderTip(
|
|
492
|
+
'Zoom in',
|
|
493
|
+
html`
|
|
494
|
+
<button
|
|
495
|
+
class="toolbar-btn"
|
|
496
|
+
@click=${() => this.fireToolbarAction('zoom-in')}
|
|
497
|
+
?disabled=${!this.zoomInitialized || this.zoom >= 1.0}
|
|
498
|
+
aria-label="Zoom in"
|
|
499
|
+
>
|
|
500
|
+
+
|
|
501
|
+
</button>
|
|
502
|
+
`
|
|
503
|
+
)}
|
|
504
|
+
<div class="toolbar-divider"></div>
|
|
505
|
+
${this.renderTip(
|
|
506
|
+
'Zoom to 100%',
|
|
507
|
+
html`
|
|
508
|
+
<button
|
|
509
|
+
class="toolbar-btn"
|
|
510
|
+
@click=${() => this.fireToolbarAction('zoom-to-full')}
|
|
511
|
+
?disabled=${!this.zoomInitialized || this.zoom >= 1.0}
|
|
512
|
+
aria-label="Zoom to 100%"
|
|
513
|
+
>
|
|
514
|
+
<temba-icon
|
|
515
|
+
name=${Icon.zoom_in}
|
|
516
|
+
size="1"
|
|
517
|
+
></temba-icon>
|
|
518
|
+
</button>
|
|
519
|
+
`
|
|
520
|
+
)}
|
|
521
|
+
<div class="toolbar-divider"></div>
|
|
522
|
+
`
|
|
523
|
+
: ''}
|
|
524
|
+
${this.renderTip(
|
|
525
|
+
'Revisions',
|
|
526
|
+
html`
|
|
527
|
+
<button
|
|
528
|
+
class="toolbar-btn ${this.revisionsActive ? 'active' : ''}"
|
|
529
|
+
@click=${() => this.fireToolbarAction('revisions')}
|
|
530
|
+
aria-label="Revisions"
|
|
531
|
+
>
|
|
532
|
+
<temba-icon
|
|
533
|
+
name=${this.isSaving ? 'progress_spinner' : 'revisions'}
|
|
534
|
+
size="1"
|
|
535
|
+
?spin=${this.isSaving}
|
|
536
|
+
></temba-icon>
|
|
537
|
+
</button>
|
|
538
|
+
`
|
|
539
|
+
)}
|
|
540
|
+
<div class="toolbar-divider"></div>
|
|
541
|
+
${this.renderTip(
|
|
542
|
+
this.renderShortcutLabel(
|
|
543
|
+
searchTargetLabel,
|
|
544
|
+
this.getSearchShortcutLabel()
|
|
545
|
+
),
|
|
546
|
+
html`
|
|
547
|
+
<button
|
|
548
|
+
class="toolbar-btn"
|
|
549
|
+
@click=${() => this.fireToolbarAction('search')}
|
|
550
|
+
?disabled=${this.searchDisabled}
|
|
551
|
+
aria-label=${searchTargetLabel}
|
|
552
|
+
>
|
|
553
|
+
<temba-icon name=${Icon.search} size="1"></temba-icon>
|
|
554
|
+
</button>
|
|
555
|
+
`
|
|
556
|
+
)}
|
|
557
|
+
</div>
|
|
558
|
+
</div>
|
|
559
|
+
`;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
private renderTranslationTools(): TemplateResult {
|
|
563
|
+
// auto translate button hidden pending backend changes
|
|
564
|
+
return html``;
|
|
565
|
+
}
|
|
566
|
+
}
|
package/src/flow/FlowSearch.ts
CHANGED
|
@@ -221,10 +221,23 @@ function getActionSearchTexts(action: Action): string[] {
|
|
|
221
221
|
return texts;
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
function
|
|
224
|
+
function getTableSearchTexts(action: Action): string[] {
|
|
225
225
|
const texts: string[] = [];
|
|
226
|
-
|
|
227
|
-
if (
|
|
226
|
+
const config = ACTION_CONFIG[action.type];
|
|
227
|
+
if (!config?.localizable) return texts;
|
|
228
|
+
const a = action as Record<string, any>;
|
|
229
|
+
for (const key of config.localizable) {
|
|
230
|
+
const val = a[key];
|
|
231
|
+
if (typeof val === 'string' && val.trim()) {
|
|
232
|
+
texts.push(val);
|
|
233
|
+
} else if (Array.isArray(val)) {
|
|
234
|
+
for (const item of val) {
|
|
235
|
+
if (typeof item === 'string' && item.trim()) {
|
|
236
|
+
texts.push(item);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
228
241
|
return texts;
|
|
229
242
|
}
|
|
230
243
|
|
|
@@ -729,20 +742,37 @@ export class FlowSearch extends LitElement {
|
|
|
729
742
|
const nodeUI = this.definition._ui?.nodes[node.uuid];
|
|
730
743
|
const nodeType = nodeUI?.type || 'execute_actions';
|
|
731
744
|
|
|
732
|
-
// Message table rows: one row per
|
|
745
|
+
// Message table rows: one row per action with localizable fields
|
|
733
746
|
if (node.actions) {
|
|
734
747
|
for (const action of node.actions) {
|
|
735
|
-
|
|
748
|
+
const actionConfig = ACTION_CONFIG[action.type];
|
|
749
|
+
if (
|
|
750
|
+
action.type !== 'send_msg' &&
|
|
751
|
+
(!actionConfig?.localizable || actionConfig.localizable.length === 0)
|
|
752
|
+
) {
|
|
736
753
|
continue;
|
|
737
754
|
}
|
|
738
755
|
|
|
739
|
-
|
|
740
|
-
const
|
|
756
|
+
// Search both original and localized texts, but only add one result per action
|
|
757
|
+
const originalTexts = getTableSearchTexts(action);
|
|
758
|
+
const localizedAction = localizeAction(
|
|
741
759
|
action,
|
|
742
760
|
langLocalization?.[action.uuid]
|
|
743
|
-
)
|
|
744
|
-
const
|
|
745
|
-
|
|
761
|
+
);
|
|
762
|
+
const localizedTexts = getTableSearchTexts(localizedAction);
|
|
763
|
+
|
|
764
|
+
// Deduplicate: combine both, originals first
|
|
765
|
+
const allTexts: string[] = [];
|
|
766
|
+
const seen = new Set<string>();
|
|
767
|
+
for (const text of [...originalTexts, ...localizedTexts]) {
|
|
768
|
+
if (!seen.has(text)) {
|
|
769
|
+
seen.add(text);
|
|
770
|
+
allTexts.push(text);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
let found = false;
|
|
775
|
+
for (const text of allTexts) {
|
|
746
776
|
const idx = text.toLowerCase().indexOf(query);
|
|
747
777
|
if (idx !== -1) {
|
|
748
778
|
results.push({
|
|
@@ -754,9 +784,11 @@ export class FlowSearch extends LitElement {
|
|
|
754
784
|
matchStart: idx,
|
|
755
785
|
matchLength: query.length
|
|
756
786
|
});
|
|
787
|
+
found = true;
|
|
757
788
|
break;
|
|
758
789
|
}
|
|
759
790
|
}
|
|
791
|
+
if (found) continue;
|
|
760
792
|
}
|
|
761
793
|
}
|
|
762
794
|
|