@nyaruka/temba-components 0.156.10 → 0.156.11

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nyaruka/temba-components",
3
- "version": "0.156.10",
3
+ "version": "0.156.11",
4
4
  "description": "Web components to support rapidpro and related projects",
5
5
  "author": "Nyaruka <code@nyaruka.coim>",
6
6
  "main": "dist/index.js",
package/src/flow/types.ts CHANGED
@@ -36,7 +36,7 @@ export const CONTEXT_MENU_SHORTCUTS: Record<FlowType, ContextMenuShortcut[]> = {
36
36
  { type: 'wait_for_response', name: 'Wait for Response', icon: 'message' }
37
37
  ],
38
38
  [FlowTypes.VOICE]: [
39
- { type: 'say_msg', name: 'Say Message', icon: 'send' },
39
+ { type: 'say_msg', name: 'Say Message', icon: 'recording' },
40
40
  { type: 'wait_for_menu', name: 'Wait for Menu', icon: 'dots-grid' }
41
41
  ],
42
42
  [FlowTypes.BACKGROUND]: [
@@ -2,12 +2,12 @@ import { TemplateResult, html, css, PropertyValues, nothing } from 'lit';
2
2
  import { FieldElement } from './FieldElement';
3
3
  import { property } from 'lit/decorators.js';
4
4
  import { Attachment, CustomEventType, Language, Shortcut } from '../interfaces';
5
+ import { Icon } from '../Icons';
5
6
  import { DEFAULT_MEDIA_ENDPOINT } from '../utils';
6
7
  import { Select } from './select/Select';
7
8
  import { MessageEditor } from './MessageEditor';
8
9
  import { ShortcutList } from '../list/ShortcutList';
9
10
  import { setCaretOffset } from '../excellent/caret-utils';
10
- import { Icon } from '../Icons';
11
11
 
12
12
  export interface ComposeValue {
13
13
  text: string;
@@ -270,6 +270,65 @@ export class Compose extends FieldElement {
270
270
  );
271
271
  }
272
272
 
273
+ private hasTranslation(iso: string): boolean {
274
+ const entry = this.langValues?.[iso];
275
+ if (!entry) return false;
276
+ return !!(
277
+ (entry.text && entry.text.trim().length > 0) ||
278
+ (entry.attachments && entry.attachments.length > 0) ||
279
+ (entry.quick_replies && entry.quick_replies.length > 0)
280
+ );
281
+ }
282
+
283
+ private getSortedLanguages(): Language[] {
284
+ if (this.languages.length === 0) return [];
285
+ const [base, ...rest] = this.languages;
286
+ const sorted = [...rest].sort((a, b) => {
287
+ const aTranslated = this.hasTranslation(a.iso);
288
+ const bTranslated = this.hasTranslation(b.iso);
289
+ if (aTranslated !== bTranslated) {
290
+ return aTranslated ? -1 : 1;
291
+ }
292
+ return 0;
293
+ });
294
+ return [base, ...sorted];
295
+ }
296
+
297
+ private renderLanguageOption = (option: Language): TemplateResult => {
298
+ const isBase =
299
+ this.languages.length > 0 && option.iso === this.languages[0].iso;
300
+
301
+ const badgeBase =
302
+ 'display:inline-flex; align-items:center; border-radius:999px; font-size:10px; font-weight:700; line-height:1;';
303
+
304
+ let badge: TemplateResult;
305
+ if (isBase) {
306
+ badge = html`<span
307
+ style="${badgeBase} background:rgba(47, 63, 82, 0.12); color:#2f3f52; padding:3px 7px;"
308
+ >Original</span
309
+ >`;
310
+ } else if (this.hasTranslation(option.iso)) {
311
+ badge = html`<span
312
+ style="${badgeBase} background:rgba(26, 127, 55, 0.15); color:#1a7f37; padding:3px 7px;"
313
+ >Translated</span
314
+ >`;
315
+ } else {
316
+ badge = html`<span
317
+ style="${badgeBase} background:transparent; color:#8a94a1; border:1px solid #c2c8d0; padding:2px 6px;"
318
+ >Missing</span
319
+ >`;
320
+ }
321
+
322
+ return html`
323
+ <div
324
+ style="display:flex; align-items:center; justify-content:space-between; gap:8px; flex:1; padding:2px 8px;"
325
+ >
326
+ <span>${option.name}</span>
327
+ ${badge}
328
+ </div>
329
+ `;
330
+ };
331
+
273
332
  public willUpdate(changed: PropertyValues): void {
274
333
  super.willUpdate(changed);
275
334
 
@@ -420,7 +479,17 @@ export class Compose extends FieldElement {
420
479
  if (editor) {
421
480
  const richEdit = editor.getRichEditor();
422
481
  if (richEdit) {
423
- richEdit.value = this.initialText;
482
+ const targetText = this.initialText;
483
+ const targetLanguage = this.currentLanguage;
484
+ richEdit.value = targetText;
485
+ const editable = richEdit.inputElement;
486
+ if (editable) {
487
+ window.setTimeout(() => {
488
+ if (this.currentLanguage === targetLanguage) {
489
+ setCaretOffset(editable, targetText.length);
490
+ }
491
+ }, 0);
492
+ }
424
493
  }
425
494
  }
426
495
  }
@@ -657,7 +726,8 @@ export class Compose extends FieldElement {
657
726
  @change=${this.handleLanguageChange}
658
727
  class="language"
659
728
  name="language"
660
- .staticOptions=${this.languages}
729
+ .staticOptions=${this.getSortedLanguages()}
730
+ .renderOption=${this.renderLanguageOption}
661
731
  valueKey="iso"
662
732
  >
663
733
  </temba-select>`
@@ -2272,7 +2272,7 @@ export class Select<T extends SelectOption> extends FieldElement {
2272
2272
  --icon-color: var(--color-text-dark);
2273
2273
  ${this.isMultiMode
2274
2274
  ? 'vertical-align: middle; background: #fff; border: 1px solid rgba(100,100,100,0.3); user-select: none; border-radius: 2px; align-items: center; flex-direction: row; flex-wrap: nowrap; margin: 2px 2px;'
2275
- : ''}
2275
+ : 'flex: 1; min-width: 0;'}
2276
2276
  ${index === this.selectedIndex
2277
2277
  ? 'background: rgba(100,100,100,0.3);'
2278
2278
  : ''}