@nyaruka/temba-components 0.156.3 → 0.156.5

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.
@@ -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
+ }
@@ -26,9 +26,13 @@ export class StickyNote extends RapidElement {
26
26
  @property({ type: Boolean })
27
27
  private removing = false;
28
28
 
29
- // On touch devices, contenteditable starts false to prevent Apple Pencil
30
- // Scribble from hijacking touches. It is set to true on explicit tap.
31
- private isTouchDevice = navigator.maxTouchPoints > 0;
29
+ // On Apple touch devices, contenteditable starts false to prevent Apple
30
+ // Pencil Scribble from hijacking touches. It is set to true on explicit tap.
31
+ // Only Apple platforms have Scribble, so we avoid disabling contenteditable
32
+ // on Windows touchscreen laptops where it would block mouse-based editing.
33
+ private isTouchDevice =
34
+ navigator.maxTouchPoints > 0 &&
35
+ /iPad|iPhone|Macintosh/.test(navigator.userAgent);
32
36
  private editingField: HTMLElement | null = null;
33
37
  private removalTimeout: number | null = null;
34
38
 
@@ -88,6 +92,24 @@ export class StickyNote extends RapidElement {
88
92
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
89
93
  }
90
94
 
95
+ :host(.drag-copy) .sticky-note {
96
+ outline: 3px dashed var(--color-primary, #3b82f6);
97
+ outline-offset: 2px;
98
+ opacity: 0.7;
99
+ }
100
+
101
+ .sticky-note.selected {
102
+ cursor: var(--shift-held-cursor, move) !important;
103
+ }
104
+
105
+ .sticky-note.selected .remove-button {
106
+ cursor: pointer !important;
107
+ }
108
+
109
+ .sticky-note.selected [contenteditable] {
110
+ cursor: text !important;
111
+ }
112
+
91
113
  /* Color themes */
92
114
  .sticky-note.yellow {
93
115
  --sticky-color: #fef08a;
@@ -191,7 +213,7 @@ export class StickyNote extends RapidElement {
191
213
  /* Drag icon */
192
214
  .sticky-title-container > .drag-handle {
193
215
  --icon-color: var(--sticky-border-color);
194
- cursor: move;
216
+ cursor: var(--shift-held-cursor, move);
195
217
  max-width: 20px;
196
218
  padding-left: 8px;
197
219
  padding-top: 10px;
@@ -769,7 +791,9 @@ export class StickyNote extends RapidElement {
769
791
  <div
770
792
  class="sticky-note ${this.data.color} ${this.dragging
771
793
  ? 'dragging'
772
- : ''} ${this.removing ? 'removing' : ''}"
794
+ : ''} ${this.removing ? 'removing' : ''} ${this.selected
795
+ ? 'selected'
796
+ : ''}"
773
797
  style="${style}"
774
798
  data-uuid="${this.uuid}"
775
799
  >
@@ -8,17 +8,14 @@ import {
8
8
  } from '../types';
9
9
  import { Node, SetContactLanguage } from '../../store/flow-definition';
10
10
  import { getStore } from '../../store/Store';
11
- import { renderClamped } from '../utils';
11
+ import { getLanguageDisplayName, renderClamped } from '../utils';
12
12
 
13
13
  export const set_contact_language: ActionConfig = {
14
14
  name: 'Update Language',
15
15
  group: ACTION_GROUPS.contacts,
16
16
  flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],
17
17
  render: (_node: Node, action: SetContactLanguage) => {
18
- const languageNames = new Intl.DisplayNames(['en'], {
19
- type: 'language'
20
- });
21
- const name = languageNames.of(action.language) || action.language;
18
+ const name = getLanguageDisplayName(action.language);
22
19
  return renderClamped(
23
20
  html`Set to <strong>${name}</strong>`,
24
21
  `Set to ${name}`
@@ -38,12 +35,9 @@ export const set_contact_language: ActionConfig = {
38
35
  const store = getStore();
39
36
  const workspace = store?.getState().workspace;
40
37
  if (workspace?.languages && Array.isArray(workspace.languages)) {
41
- const languageNames = new Intl.DisplayNames(['en'], {
42
- type: 'language'
43
- });
44
38
  return workspace.languages.map((languageCode: string) => ({
45
39
  value: languageCode,
46
- name: languageNames.of(languageCode) || languageCode
40
+ name: getLanguageDisplayName(languageCode)
47
41
  }));
48
42
  }
49
43
  return [];
@@ -53,14 +47,11 @@ export const set_contact_language: ActionConfig = {
53
47
  toFormData: (action: SetContactLanguage) => {
54
48
  // Convert the language code back to the option object format expected by the form
55
49
  if (action.language) {
56
- const languageNames = new Intl.DisplayNames(['en'], {
57
- type: 'language'
58
- });
59
50
  return {
60
51
  language: [
61
52
  {
62
53
  value: action.language,
63
- name: languageNames.of(action.language) || action.language
54
+ name: getLanguageDisplayName(action.language)
64
55
  }
65
56
  ],
66
57
  uuid: action.uuid
package/src/flow/utils.ts CHANGED
@@ -6,6 +6,17 @@ import { tokenize, TokenType } from '../excellent/tokenizer';
6
6
  import { TOKEN_COLORS } from '../excellent/token-styles';
7
7
  import { messageParser, sessionParser } from '../excellent/helpers';
8
8
 
9
+ const languageNames = new Intl.DisplayNames(['en'], { type: 'language' });
10
+
11
+ export function getLanguageDisplayName(code: string): string {
12
+ if (code === 'und') return 'Unknown';
13
+ try {
14
+ return languageNames.of(code) || code;
15
+ } catch {
16
+ return code;
17
+ }
18
+ }
19
+
9
20
  const IS_MAC =
10
21
  typeof navigator !== 'undefined' &&
11
22
  /Mac|iPod|iPhone|iPad/.test(navigator.platform);
@@ -168,6 +168,7 @@ export class Select<T extends SelectOption> extends FieldElement {
168
168
  }
169
169
 
170
170
  .selected {
171
+ flex: 1;
171
172
  display: flex;
172
173
  flex-direction: row;
173
174
  align-items: stretch;