@nyaruka/temba-components 0.113.0 → 0.114.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.
Files changed (130) hide show
  1. package/CHANGELOG.md +21 -2
  2. package/demo/index.html +1 -1
  3. package/dist/temba-components.js +793 -966
  4. package/dist/temba-components.js.map +1 -1
  5. package/out-tsc/src/aliaseditor/AliasEditor.js.map +1 -1
  6. package/out-tsc/src/button/Button.js +6 -2
  7. package/out-tsc/src/button/Button.js.map +1 -1
  8. package/out-tsc/src/chat/Chat.js +29 -7
  9. package/out-tsc/src/chat/Chat.js.map +1 -1
  10. package/out-tsc/src/compose/Compose.js +10 -5
  11. package/out-tsc/src/compose/Compose.js.map +1 -1
  12. package/out-tsc/src/contacts/ContactChat.js +240 -114
  13. package/out-tsc/src/contacts/ContactChat.js.map +1 -1
  14. package/out-tsc/src/contacts/ContactFieldEditor.js.map +1 -1
  15. package/out-tsc/src/contacts/events.js.map +1 -1
  16. package/out-tsc/src/contacts/helpers.js +5 -1
  17. package/out-tsc/src/contacts/helpers.js.map +1 -1
  18. package/out-tsc/src/contactsearch/ContactSearch.js +1 -1
  19. package/out-tsc/src/contactsearch/ContactSearch.js.map +1 -1
  20. package/out-tsc/src/dropdown/Dropdown.js +121 -108
  21. package/out-tsc/src/dropdown/Dropdown.js.map +1 -1
  22. package/out-tsc/src/interfaces.js +2 -0
  23. package/out-tsc/src/interfaces.js.map +1 -1
  24. package/out-tsc/src/list/ContentMenu.js +11 -8
  25. package/out-tsc/src/list/ContentMenu.js.map +1 -1
  26. package/out-tsc/src/list/RunList.js.map +1 -1
  27. package/out-tsc/src/list/TembaList.js +21 -14
  28. package/out-tsc/src/list/TembaList.js.map +1 -1
  29. package/out-tsc/src/list/TembaMenu.js +11 -12
  30. package/out-tsc/src/list/TembaMenu.js.map +1 -1
  31. package/out-tsc/src/list/TicketList.js +10 -0
  32. package/out-tsc/src/list/TicketList.js.map +1 -1
  33. package/out-tsc/src/omnibox/Omnibox.js +33 -90
  34. package/out-tsc/src/omnibox/Omnibox.js.map +1 -1
  35. package/out-tsc/src/options/Options.js +49 -47
  36. package/out-tsc/src/options/Options.js.map +1 -1
  37. package/out-tsc/src/select/PopupSelect.js +57 -0
  38. package/out-tsc/src/select/PopupSelect.js.map +1 -0
  39. package/out-tsc/src/select/Select.js +194 -144
  40. package/out-tsc/src/select/Select.js.map +1 -1
  41. package/out-tsc/src/select/UserSelect.js +67 -0
  42. package/out-tsc/src/select/UserSelect.js.map +1 -0
  43. package/out-tsc/src/store/Store.js +65 -14
  44. package/out-tsc/src/store/Store.js.map +1 -1
  45. package/out-tsc/src/tabpane/TabPane.js +72 -115
  46. package/out-tsc/src/tabpane/TabPane.js.map +1 -1
  47. package/out-tsc/src/textinput/TextInput.js +1 -0
  48. package/out-tsc/src/textinput/TextInput.js.map +1 -1
  49. package/out-tsc/src/user/TembaUser.js +24 -37
  50. package/out-tsc/src/user/TembaUser.js.map +1 -1
  51. package/out-tsc/src/utils/index.js +13 -6
  52. package/out-tsc/src/utils/index.js.map +1 -1
  53. package/out-tsc/temba-modules.js +4 -2
  54. package/out-tsc/temba-modules.js.map +1 -1
  55. package/out-tsc/test/temba-omnibox.test.js +43 -4
  56. package/out-tsc/test/temba-omnibox.test.js.map +1 -1
  57. package/out-tsc/test/temba-select.test.js +121 -65
  58. package/out-tsc/test/temba-select.test.js.map +1 -1
  59. package/out-tsc/test/utils.test.js +4 -0
  60. package/out-tsc/test/utils.test.js.map +1 -1
  61. package/package.json +1 -1
  62. package/screenshots/truth/compose/attachments-tab.png +0 -0
  63. package/screenshots/truth/compose/attachments-with-files-focused.png +0 -0
  64. package/screenshots/truth/compose/attachments-with-files.png +0 -0
  65. package/screenshots/truth/compose/intial-text.png +0 -0
  66. package/screenshots/truth/compose/no-counter.png +0 -0
  67. package/screenshots/truth/compose/wraps-text-and-spaces.png +0 -0
  68. package/screenshots/truth/compose/wraps-text-and-url.png +0 -0
  69. package/screenshots/truth/compose/wraps-text-no-spaces.png +0 -0
  70. package/screenshots/truth/contacts/chat-failure.png +0 -0
  71. package/screenshots/truth/contacts/chat-for-active-contact.png +0 -0
  72. package/screenshots/truth/contacts/chat-for-archived-contact.png +0 -0
  73. package/screenshots/truth/contacts/chat-for-blocked-contact.png +0 -0
  74. package/screenshots/truth/contacts/chat-for-stopped-contact.png +0 -0
  75. package/screenshots/truth/contacts/chat-sends-attachments-only.png +0 -0
  76. package/screenshots/truth/contacts/chat-sends-text-and-attachments.png +0 -0
  77. package/screenshots/truth/contacts/chat-sends-text-only.png +0 -0
  78. package/screenshots/truth/content-menu/item-no-buttons.png +0 -0
  79. package/screenshots/truth/content-menu/items-and-buttons.png +0 -0
  80. package/screenshots/truth/omnibox/selected.png +0 -0
  81. package/screenshots/truth/select/enabled-multi-selection.png +0 -0
  82. package/screenshots/truth/select/endpoint-initial-value-updated.png +0 -0
  83. package/screenshots/truth/select/endpoint-initial-value.png +0 -0
  84. package/screenshots/truth/select/expressions.png +0 -0
  85. package/screenshots/truth/select/functions.png +0 -0
  86. package/screenshots/truth/select/initial-value.png +0 -0
  87. package/screenshots/truth/select/multi-with-endpoint.png +0 -0
  88. package/screenshots/truth/select/multiple-initial-values.png +0 -0
  89. package/screenshots/truth/select/selected-multi-test.png +0 -0
  90. package/screenshots/truth/select/static-initial-value.png +0 -0
  91. package/screenshots/truth/select/static-initial-via-selected.png +0 -0
  92. package/screenshots/truth/select/value-initial.png +0 -0
  93. package/src/aliaseditor/AliasEditor.ts +1 -1
  94. package/src/button/Button.ts +6 -2
  95. package/src/chat/Chat.ts +28 -6
  96. package/src/compose/Compose.ts +11 -6
  97. package/src/contacts/ContactChat.ts +260 -118
  98. package/src/contacts/ContactFieldEditor.ts +1 -1
  99. package/src/contacts/events.ts +1 -0
  100. package/src/contacts/helpers.ts +8 -1
  101. package/src/contactsearch/ContactSearch.ts +3 -3
  102. package/src/dropdown/Dropdown.ts +142 -103
  103. package/src/interfaces.ts +4 -1
  104. package/src/list/ContentMenu.ts +11 -9
  105. package/src/list/RunList.ts +3 -1
  106. package/src/list/TembaList.ts +24 -14
  107. package/src/list/TembaMenu.ts +14 -15
  108. package/src/list/TicketList.ts +11 -0
  109. package/src/omnibox/Omnibox.ts +34 -95
  110. package/src/options/Options.ts +57 -60
  111. package/src/select/PopupSelect.ts +53 -0
  112. package/src/select/Select.ts +182 -112
  113. package/src/select/UserSelect.ts +71 -0
  114. package/src/store/Store.ts +70 -21
  115. package/src/tabpane/TabPane.ts +79 -113
  116. package/src/textinput/TextInput.ts +1 -0
  117. package/src/user/TembaUser.ts +30 -41
  118. package/src/utils/index.ts +12 -8
  119. package/temba-modules.ts +4 -2
  120. package/test/temba-omnibox.test.ts +56 -4
  121. package/test/temba-select.test.ts +170 -56
  122. package/test/utils.test.ts +5 -0
  123. package/test-assets/select/omnibox.json +55 -0
  124. package/web-test-runner.config.mjs +16 -4
  125. package/out-tsc/src/contacts/ContactTickets.js +0 -462
  126. package/out-tsc/src/contacts/ContactTickets.js.map +0 -1
  127. package/out-tsc/test/temba-contact-tickets.test.js +0 -36
  128. package/out-tsc/test/temba-contact-tickets.test.js.map +0 -1
  129. package/src/contacts/ContactTickets.ts +0 -490
  130. package/test/temba-contact-tickets.test.ts +0 -52
@@ -13,62 +13,10 @@ import { Icon } from '../vectoricon';
13
13
  import { msg } from '@lit/localize';
14
14
  const LOOK_AHEAD = 20;
15
15
  export class Select extends FormElement {
16
- constructor() {
17
- super(...arguments);
18
- this.hiddenInputs = [];
19
- this.inputStyle = {};
20
- this.multi = false;
21
- this.searchOnFocus = false;
22
- this.placeholder = '';
23
- this.name = '';
24
- this.nameKey = 'name';
25
- this.valueKey = 'value';
26
- this.queryParam = null;
27
- this.input = '';
28
- this.visibleOptions = [];
29
- this.completionOptions = [];
30
- this.quietMillis = 0;
31
- this.searchable = false;
32
- this.cache = true;
33
- this.cacheKey = '';
34
- this.focused = false;
35
- this.disabled = false;
36
- this.selectedIndex = -1;
37
- this.anchorPosition = { left: 0, top: 0 };
38
- this.tags = false;
39
- this.flavor = 'default';
40
- this.infoText = '';
41
- this.values = [];
42
- this.getName = (option) => option[this.nameKey || 'name'];
43
- this.isMatch = (option, q) => {
44
- const name = this.getName(option) || '';
45
- return name.toLowerCase().indexOf(q) > -1;
46
- };
47
- this.getValue = (option) => option[this.valueKey || 'value'] || option.id;
48
- this.sortFunction = null;
49
- this.renderOptionDetail = () => html ``;
50
- this.renderSelectedItem = this.renderSelectedItemDefault;
51
- this.createArbitraryOption = this.createArbitraryOptionDefault;
52
- this.getOptions = this.getOptionsDefault;
53
- this.prepareOptions = (options) => options;
54
- this.isComplete = this.isCompleteDefault;
55
- this.staticOptions = [];
56
- this.alphaSort = (a, b) => {
57
- // by default, all endpoint values are sorted by name
58
- if (this.endpoint) {
59
- return this.getName(a).localeCompare(this.getName(b));
60
- }
61
- return 0;
62
- };
63
- this.next = null;
64
- this.lruCache = lru(20, 60000);
65
- this.getNameInternal = (option) => {
66
- return this.getName(option);
67
- };
68
- }
69
16
  static get styles() {
70
17
  return css `
71
18
  :host {
19
+ --transition-speed: 0;
72
20
  font-family: var(--font-family);
73
21
  transition: all ease-in-out var(--transition-speed);
74
22
  display: inline;
@@ -81,6 +29,7 @@ export class Select extends FormElement {
81
29
  temba-options {
82
30
  --temba-options-font-size: var(--temba-select-selected-font-size);
83
31
  --icon-color: var(--color-text-dark);
32
+ --color-options-bg: #fff;
84
33
  }
85
34
 
86
35
  :host:focus {
@@ -361,8 +310,69 @@ export class Select extends FormElement {
361
310
  }
362
311
  `;
363
312
  }
313
+ constructor() {
314
+ super();
315
+ this.hiddenInputs = [];
316
+ this.inputStyle = {};
317
+ this.multi = false;
318
+ this.searchOnFocus = false;
319
+ this.placeholder = '';
320
+ this.name = '';
321
+ this.nameKey = 'name';
322
+ this.valueKey = 'value';
323
+ this.queryParam = null;
324
+ this.input = '';
325
+ this.visibleOptions = [];
326
+ this.completionOptions = [];
327
+ this.quietMillis = 0;
328
+ this.searchable = false;
329
+ this.cache = true;
330
+ this.cacheKey = '';
331
+ this.focused = false;
332
+ this.disabled = false;
333
+ this.selectedIndex = -1;
334
+ this.anchorPosition = { left: 0, top: 0 };
335
+ this.tags = false;
336
+ this.flavor = 'default';
337
+ this.infoText = '';
338
+ this.values = [];
339
+ this.getName = (option) => option[this.nameKey || 'name'];
340
+ this.isMatch = this.isMatchDefault;
341
+ this.getValue = (option) => option[this.valueKey || 'value'] || option.id;
342
+ this.sortFunction = null;
343
+ this.renderOptionDetail = () => html ``;
344
+ this.renderSelectedItem = this.renderSelectedItemDefault;
345
+ this.createArbitraryOption = this.createArbitraryOptionDefault;
346
+ this.getOptions = this.getOptionsDefault;
347
+ this.prepareOptions = this.prepareOptionsDefault;
348
+ this.isComplete = this.isCompleteDefault;
349
+ this.staticOptions = [];
350
+ this.alphaSort = (a, b) => {
351
+ // by default, all endpoint values are sorted by name
352
+ if (this.endpoint) {
353
+ return this.getName(a).localeCompare(this.getName(b));
354
+ }
355
+ return 0;
356
+ };
357
+ this.next = null;
358
+ this.lruCache = lru(20, 60000);
359
+ this.getNameInternal = (option) => {
360
+ return this.getName(option);
361
+ };
362
+ this.renderOptionDefault = this.renderOptionDefault.bind(this);
363
+ this.renderSelectedItemDefault = this.renderSelectedItemDefault.bind(this);
364
+ this.prepareOptionsDefault = this.prepareOptionsDefault.bind(this);
365
+ this.isMatchDefault = this.isMatchDefault.bind(this);
366
+ }
367
+ prepareOptionsDefault(options) {
368
+ return options;
369
+ }
370
+ isMatchDefault(option, q) {
371
+ const name = this.getName(option) || '';
372
+ return name.toLowerCase().indexOf(q) > -1;
373
+ }
364
374
  handleSlotChange() {
365
- if (this.staticOptions.length === 0) {
375
+ if (this.staticOptions && this.staticOptions.length === 0) {
366
376
  for (const child of this.children) {
367
377
  if (child.tagName === 'TEMBA-OPTION') {
368
378
  const option = {};
@@ -370,10 +380,23 @@ export class Select extends FormElement {
370
380
  option[attribute.name] = attribute.value;
371
381
  }
372
382
  if (option) {
383
+ let selected = false;
384
+ // if the option is marked as selected then accept it
385
+ if (option['selected'] !== undefined) {
386
+ delete option['selected'];
387
+ selected = true;
388
+ }
389
+ // the option value might also match the widget value
390
+ const selectValue = this.value || this.getAttribute('value');
391
+ if (selectValue) {
392
+ const optionValue = this.getValue(option);
393
+ if (optionValue == selectValue) {
394
+ selected = true;
395
+ }
396
+ }
373
397
  this.staticOptions.push(option);
374
- if (child.getAttribute('selected') !== null ||
375
- this.getValue(option) == this.value) {
376
- if (this.getAttribute('multi') !== null) {
398
+ if (selected) {
399
+ if (this.multi) {
377
400
  this.addValue(option);
378
401
  }
379
402
  else {
@@ -384,43 +407,47 @@ export class Select extends FormElement {
384
407
  }
385
408
  }
386
409
  }
387
- this.checkSelectedOption();
388
410
  if (this.searchable && this.staticOptions.length === 0) {
389
411
  this.quietMillis = 200;
390
412
  }
391
413
  }
392
414
  checkSelectedOption() {
393
- if (this.values.length === 0 && (!this.placeholder || this.value)) {
394
- if (this.staticOptions.length == 0 && this.endpoint) {
395
- const value = this.value;
396
- // see if we need fetch to select an option
397
- fetchResults(this.endpoint).then((results) => {
398
- if (results && results.length > 0) {
399
- if (value) {
400
- // if we started with a value, see if we can find it in the results
401
- const existing = results.find((option) => {
402
- return this.getValue(option) === value;
403
- });
404
- if (existing) {
405
- this.setValues([existing]);
406
- return;
407
- }
415
+ // see if we need fetch to select an option
416
+ if (this.value &&
417
+ this.values.length == 0 &&
418
+ this.staticOptions.length == 0 &&
419
+ this.endpoint) {
420
+ const value = this.value;
421
+ this.resolving = true;
422
+ fetchResults(this.endpoint).then((results) => {
423
+ if (results && results.length > 0) {
424
+ if (value) {
425
+ // if we started with a value, see if we can find it in the results
426
+ const existing = results.find((option) => {
427
+ return this.getValue(option) === value;
428
+ });
429
+ if (existing) {
430
+ this.resolving = false;
431
+ this.fetching = false;
432
+ this.setValues([existing]);
433
+ return;
408
434
  }
409
- this.setValues([results[0]]);
410
435
  }
411
- });
436
+ this.setValues([results[0]]);
437
+ this.resolving = false;
438
+ }
439
+ });
440
+ }
441
+ else if (this.staticOptions.length > 0) {
442
+ if (this.getAttribute('multi') !== null) {
443
+ this.addValue(this.staticOptions[0]);
412
444
  }
413
- else if (this.staticOptions.length > 0) {
414
- if (this.getAttribute('multi') !== null) {
415
- this.addValue(this.staticOptions[0]);
445
+ else {
446
+ if (this.getAttribute('value')) {
447
+ this.setSelectedValue(this.getAttribute('value'));
416
448
  }
417
449
  else {
418
- if (this.getAttribute('value')) {
419
- this.setSelectedValue(this.getAttribute('value'));
420
- }
421
- else {
422
- this.setValues([this.staticOptions[0]]);
423
- }
450
+ this.setValues([this.staticOptions[0]]);
424
451
  }
425
452
  }
426
453
  }
@@ -430,31 +457,30 @@ export class Select extends FormElement {
430
457
  this.anchorElement = this.shadowRoot.querySelector('.select-container');
431
458
  this.anchorExpressions = this.shadowRoot.querySelector('#anchor');
432
459
  this.shadowRoot.addEventListener('slotchange', this.handleSlotChange.bind(this));
433
- this.checkSelectedOption();
434
460
  }
435
- updated(changedProperties) {
436
- super.updated(changedProperties);
437
- if (changedProperties.has('sorted')) {
461
+ updated(changes) {
462
+ super.updated(changes);
463
+ if (changes.has('sorted')) {
438
464
  this.sortFunction = this.sorted ? this.alphaSort : null;
439
465
  }
440
- if (changedProperties.has('values')) {
466
+ if (changes.has('value')) {
467
+ if (this.value && !this.values.length) {
468
+ this.setSelectedValue(this.value);
469
+ }
470
+ }
471
+ if (changes.has('values')) {
441
472
  this.updateInputs();
442
- if (this.multi ||
443
- this.values.length === 1 ||
444
- // fire change if being cleared
445
- (this.values.length == 0 &&
446
- changedProperties.get('values') &&
447
- changedProperties.get('values').length > 0)) {
473
+ if (this.hasChanges(changes.get('values'))) {
448
474
  this.fireEvent('change');
449
475
  }
450
476
  }
451
477
  // if our cache key changes, clear it out
452
- if (changedProperties.has('cacheKey')) {
478
+ if (changes.has('cacheKey')) {
453
479
  this.lruCache.clear();
454
480
  }
455
- if (changedProperties.has('input') &&
456
- !changedProperties.has('values') &&
457
- !changedProperties.has('options') &&
481
+ if (changes.has('input') &&
482
+ !changes.has('values') &&
483
+ !changes.has('options') &&
458
484
  this.focused) {
459
485
  if (this.lastQuery) {
460
486
  window.clearTimeout(this.lastQuery);
@@ -468,14 +494,13 @@ export class Select extends FormElement {
468
494
  }
469
495
  }, this.quietMillis);
470
496
  }
471
- if (this.endpoint && changedProperties.has('fetching')) {
497
+ if (this.endpoint && changes.has('fetching')) {
472
498
  if (!this.fetching && !this.isPastFetchThreshold()) {
473
499
  this.fireCustomEvent(CustomEventType.FetchComplete);
474
500
  }
475
501
  }
476
502
  // if our cursor changed, lets make sure our scrollbox is showing it
477
- if ((changedProperties.has('cursorIndex') ||
478
- changedProperties.has('visibleOptions')) &&
503
+ if ((changes.has('cursorIndex') || changes.has('visibleOptions')) &&
479
504
  this.endpoint &&
480
505
  !this.fetching) {
481
506
  if (this.isPastFetchThreshold()) {
@@ -487,10 +512,6 @@ export class Select extends FormElement {
487
512
  }
488
513
  }
489
514
  }
490
- // if they set an inital value, look through our static options for it
491
- if (changedProperties.has('value') && this.value && !this.values.length) {
492
- this.setSelectedValue(this.value);
493
- }
494
515
  // default to the first option if we don't have a placeholder
495
516
  if (this.values.length === 0 &&
496
517
  !this.placeholder &&
@@ -498,6 +519,25 @@ export class Select extends FormElement {
498
519
  this.setValues([this.staticOptions[0]]);
499
520
  }
500
521
  }
522
+ hasChanges(prev) {
523
+ if (prev === undefined) {
524
+ return false;
525
+ }
526
+ let prevValues = undefined;
527
+ if (prev !== undefined) {
528
+ prevValues = (prev || [])
529
+ .map((option) => {
530
+ return this.getValue(option);
531
+ })
532
+ .join(',');
533
+ }
534
+ const newValues = (this.values || [])
535
+ .map((option) => {
536
+ return this.getValue(option);
537
+ })
538
+ .join(',');
539
+ return prevValues !== newValues;
540
+ }
501
541
  setSelectedValue(value) {
502
542
  if (this.staticOptions.length > 0) {
503
543
  const existing = this.staticOptions.find((option) => {
@@ -507,6 +547,9 @@ export class Select extends FormElement {
507
547
  this.setValues([existing]);
508
548
  }
509
549
  }
550
+ else {
551
+ this.checkSelectedOption();
552
+ }
510
553
  }
511
554
  updateInputs() {
512
555
  for (let ele = null; (ele = this.hiddenInputs.pop());) {
@@ -560,6 +603,8 @@ export class Select extends FormElement {
560
603
  (this.cursorIndex || 0) > this.visibleOptions.length - LOOK_AHEAD);
561
604
  }
562
605
  handleOptionSelection(event) {
606
+ event.preventDefault();
607
+ event.stopPropagation();
563
608
  const selected = event.detail.selected;
564
609
  // check if we should post it
565
610
  if (selected.post && this.endpoint) {
@@ -607,7 +652,7 @@ export class Select extends FormElement {
607
652
  return null;
608
653
  }
609
654
  open() {
610
- this.requestUpdate('input');
655
+ this.shadowRoot.querySelector('.select-container').click();
611
656
  }
612
657
  isOpen() {
613
658
  return this.visibleOptions.length > 0;
@@ -698,10 +743,6 @@ export class Select extends FormElement {
698
743
  this.completionOptions = [];
699
744
  if (!this.fetching) {
700
745
  this.fetching = true;
701
- // make sure we cancel any previous request
702
- // if (this.cancelToken) {
703
- // this.cancelToken.cancel();
704
- // }
705
746
  const options = [...this.staticOptions];
706
747
  const q = (query || '').trim().toLowerCase();
707
748
  if (this.tags && q) {
@@ -815,9 +856,6 @@ export class Select extends FormElement {
815
856
  handleFocus() {
816
857
  if (!this.focused && this.visibleOptions.length === 0) {
817
858
  this.focused = true;
818
- if (this.searchOnFocus && !this.removingSelection) {
819
- this.requestUpdate('input');
820
- }
821
859
  }
822
860
  }
823
861
  handleBlur() {
@@ -941,30 +979,33 @@ export class Select extends FormElement {
941
979
  ];
942
980
  }
943
981
  handleArrowClick(event) {
944
- event.preventDefault();
945
- event.stopPropagation();
946
- if (this.visibleOptions.length > 0) {
947
- this.visibleOptions = [];
948
- }
949
- else {
950
- this.handleContainerClick(event);
982
+ if (this.isOpen()) {
983
+ event.preventDefault();
984
+ event.stopPropagation();
985
+ this.blur();
951
986
  }
952
987
  }
953
- renderSelectedItemDefault(option) {
988
+ renderOptionDefault(option) {
954
989
  if (!option) {
955
990
  return null;
956
991
  }
992
+ // special case for icons on any option type
993
+ const icon = option.icon;
957
994
  return html `
958
995
  <div class="option-name" style="display:flex">
959
- ${option.icon
996
+ ${icon
960
997
  ? html `<temba-icon
961
- name="${option.icon}"
998
+ name="${icon}"
962
999
  style="margin-right:0.5em;"
963
1000
  ></temba-icon>`
964
1001
  : null}<span>${this.getName(option)}</span>
965
1002
  </div>
966
1003
  `;
967
1004
  }
1005
+ renderSelectedItemDefault(option) {
1006
+ const renderFn = this.renderOption || this.renderOptionDefault;
1007
+ return renderFn(option, true);
1008
+ }
968
1009
  serializeValue(value) {
969
1010
  // static options just use their value
970
1011
  if (!this.jsonValue && (this.staticOptions.length > 0 || this.tags)) {
@@ -974,8 +1015,8 @@ export class Select extends FormElement {
974
1015
  }
975
1016
  setSelection(value) {
976
1017
  for (const option of this.staticOptions) {
977
- if (this.getValue(option.value) === value) {
978
- if (this.values.length === 0 || this.values[0].value !== '' + value) {
1018
+ if (this.getValue(option) === value) {
1019
+ if (this.values.length === 0 || this.values[0].value != '' + value) {
979
1020
  this.setValues([option]);
980
1021
  }
981
1022
  return;
@@ -990,29 +1031,37 @@ export class Select extends FormElement {
990
1031
  this.visibleOptions = [];
991
1032
  this.requestUpdate();
992
1033
  }
1034
+ this.fireCustomEvent(CustomEventType.Selection, {
1035
+ selected: null
1036
+ });
993
1037
  }
994
1038
  setValues(values) {
1039
+ const oldValues = this.values;
995
1040
  this.values = values;
996
- this.requestUpdate('values');
1041
+ this.requestUpdate('values', oldValues);
997
1042
  }
998
1043
  addValue(value) {
1044
+ const oldValues = [...this.values];
999
1045
  this.values.push(value);
1000
- this.requestUpdate('values');
1046
+ this.requestUpdate('values', oldValues);
1001
1047
  }
1002
1048
  removeValue(valueToRemove) {
1049
+ const oldValues = [...this.values];
1003
1050
  const idx = this.values.indexOf(valueToRemove);
1004
1051
  if (idx > -1) {
1005
1052
  this.values.splice(idx, 1);
1006
1053
  }
1007
- this.requestUpdate('values');
1054
+ this.requestUpdate('values', oldValues);
1008
1055
  }
1009
1056
  popValue() {
1057
+ const oldValues = [...this.values];
1010
1058
  this.values.pop();
1011
- this.requestUpdate('values');
1059
+ this.requestUpdate('values', oldValues);
1012
1060
  }
1013
1061
  clear() {
1062
+ const oldValues = this.values;
1014
1063
  this.values = [];
1015
- this.requestUpdate('values');
1064
+ this.requestUpdate('values', oldValues);
1016
1065
  }
1017
1066
  render() {
1018
1067
  const placeholder = this.values.length === 0 ? this.placeholder : '';
@@ -1081,11 +1130,15 @@ export class Select extends FormElement {
1081
1130
  class="select-container ${classes}"
1082
1131
  @click=${this.handleContainerClick}
1083
1132
  >
1084
-
1085
- <div class="left-side">
1133
+ <div class="left-side" >
1086
1134
  <slot name="prefix"></slot>
1087
- <div class="selected">
1088
- ${!this.multi ? input : null}
1135
+ <div class="selected" >
1136
+ ${this.resolving
1137
+ ? html `<temba-loading
1138
+ style="margin-left:1em"
1139
+ ></temba-loading>`
1140
+ : null}
1141
+ ${!this.multi && !this.resolving ? input : null}
1089
1142
  ${this.values.map((selected, index) => html `
1090
1143
  <div
1091
1144
  class="selected-item ${index === this.selectedIndex
@@ -1097,12 +1150,6 @@ export class Select extends FormElement {
1097
1150
  <div
1098
1151
  class="remove-item"
1099
1152
  style="margin-top:1px"
1100
- @mousedown=${() => {
1101
- this.removingSelection = true;
1102
- }}
1103
- @mouseup=${() => {
1104
- this.removingSelection = false;
1105
- }}
1106
1153
  @click=${(evt) => {
1107
1154
  evt.preventDefault();
1108
1155
  evt.stopPropagation();
@@ -1129,7 +1176,7 @@ export class Select extends FormElement {
1129
1176
  <slot name="right"></slot>
1130
1177
  ${!this.tags
1131
1178
  ? html `<div
1132
- class="right-side"
1179
+ class="right-side arrow"
1133
1180
  style="display:block;margin-right:5px"
1134
1181
  @click=${this.handleArrowClick}
1135
1182
  >
@@ -1153,13 +1200,13 @@ export class Select extends FormElement {
1153
1200
  .cursorIndex=${this.cursorIndex}
1154
1201
  .renderOptionDetail=${this.renderOptionDetail}
1155
1202
  .renderOptionName=${this.renderOptionName}
1156
- .renderOption=${this.renderOption}
1203
+ .renderOption=${this.renderOption || this.renderOptionDefault}
1157
1204
  .anchorTo=${this.anchorElement}
1158
1205
  .options=${this.visibleOptions}
1159
1206
  .spaceSelect=${this.spaceSelect}
1160
1207
  .nameKey=${this.nameKey}
1161
1208
  .getName=${this.getNameInternal}
1162
- static-width=${this.optionWidth}
1209
+ ?static-width=${this.optionWidth}
1163
1210
  ?anchor-right=${this.anchorRight}
1164
1211
  ?visible=${this.visibleOptions.length > 0}
1165
1212
  ></temba-options>
@@ -1234,6 +1281,9 @@ __decorate([
1234
1281
  __decorate([
1235
1282
  property({ type: Boolean })
1236
1283
  ], Select.prototype, "fetching", void 0);
1284
+ __decorate([
1285
+ property({ type: Boolean })
1286
+ ], Select.prototype, "resolving", void 0);
1237
1287
  __decorate([
1238
1288
  property({ type: Boolean })
1239
1289
  ], Select.prototype, "searchable", void 0);