@nyaruka/temba-components 0.122.0 → 0.124.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 (262) hide show
  1. package/.github/copilot-instructions.md +181 -0
  2. package/.github/workflows/build.yml +3 -3
  3. package/.github/workflows/cla.yml +6 -6
  4. package/.github/workflows/copilot-setup-steps.yml +86 -0
  5. package/CHANGELOG.md +44 -0
  6. package/demo/drag-drop-demo.html +141 -0
  7. package/demo/index.html +57 -0
  8. package/demo/test-drag-drop.html +94 -0
  9. package/dist/locales/es.js +1 -0
  10. package/dist/locales/es.js.map +1 -1
  11. package/dist/locales/fr.js +1 -0
  12. package/dist/locales/fr.js.map +1 -1
  13. package/dist/locales/pt.js +1 -0
  14. package/dist/locales/pt.js.map +1 -1
  15. package/dist/temba-components.js +366 -247
  16. package/dist/temba-components.js.map +1 -1
  17. package/out-tsc/src/chart/TembaChart.js +81 -14
  18. package/out-tsc/src/chart/TembaChart.js.map +1 -1
  19. package/out-tsc/src/fields/FieldManager.js +27 -34
  20. package/out-tsc/src/fields/FieldManager.js.map +1 -1
  21. package/out-tsc/src/list/RunList.js +13 -8
  22. package/out-tsc/src/list/RunList.js.map +1 -1
  23. package/out-tsc/src/list/SortableList.js +257 -60
  24. package/out-tsc/src/list/SortableList.js.map +1 -1
  25. package/out-tsc/src/locales/es.js +1 -0
  26. package/out-tsc/src/locales/es.js.map +1 -1
  27. package/out-tsc/src/locales/fr.js +1 -0
  28. package/out-tsc/src/locales/fr.js.map +1 -1
  29. package/out-tsc/src/locales/pt.js +1 -0
  30. package/out-tsc/src/locales/pt.js.map +1 -1
  31. package/out-tsc/src/omnibox/Omnibox.js +1 -1
  32. package/out-tsc/src/omnibox/Omnibox.js.map +1 -1
  33. package/out-tsc/src/options/Options.js +36 -13
  34. package/out-tsc/src/options/Options.js.map +1 -1
  35. package/out-tsc/src/select/Select.js +226 -43
  36. package/out-tsc/src/select/Select.js.map +1 -1
  37. package/out-tsc/src/store/AppState.js +3 -3
  38. package/out-tsc/src/store/AppState.js.map +1 -1
  39. package/out-tsc/src/utils/index.js +6 -1
  40. package/out-tsc/src/utils/index.js.map +1 -1
  41. package/out-tsc/src/vectoricon/VectorIcon.js +2 -1
  42. package/out-tsc/src/vectoricon/VectorIcon.js.map +1 -1
  43. package/out-tsc/src/webchat/WebChat.js +5 -2
  44. package/out-tsc/src/webchat/WebChat.js.map +1 -1
  45. package/out-tsc/temba-modules.js +0 -2
  46. package/out-tsc/temba-modules.js.map +1 -1
  47. package/out-tsc/test/temba-appstate-language.test.js +176 -0
  48. package/out-tsc/test/temba-appstate-language.test.js.map +1 -0
  49. package/out-tsc/test/temba-chart.test.js +125 -0
  50. package/out-tsc/test/temba-chart.test.js.map +1 -1
  51. package/out-tsc/test/temba-dropdown.test.js +317 -0
  52. package/out-tsc/test/temba-dropdown.test.js.map +1 -0
  53. package/out-tsc/test/temba-flow-editor-node.test.js +273 -0
  54. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -0
  55. package/out-tsc/test/temba-flow-editor.test.js +244 -0
  56. package/out-tsc/test/temba-flow-editor.test.js.map +1 -0
  57. package/out-tsc/test/temba-flow-plumber.test.js +145 -0
  58. package/out-tsc/test/temba-flow-plumber.test.js.map +1 -0
  59. package/out-tsc/test/temba-flow-render.test.js +171 -0
  60. package/out-tsc/test/temba-flow-render.test.js.map +1 -0
  61. package/out-tsc/test/temba-omnibox.test.js +2 -3
  62. package/out-tsc/test/temba-omnibox.test.js.map +1 -1
  63. package/out-tsc/test/temba-run-list.test.js +588 -0
  64. package/out-tsc/test/temba-run-list.test.js.map +1 -0
  65. package/out-tsc/test/temba-select.test.js +149 -52
  66. package/out-tsc/test/temba-select.test.js.map +1 -1
  67. package/out-tsc/test/temba-sortable-list.test.js +91 -15
  68. package/out-tsc/test/temba-sortable-list.test.js.map +1 -1
  69. package/out-tsc/test/temba-toast.test.js +299 -0
  70. package/out-tsc/test/temba-toast.test.js.map +1 -0
  71. package/out-tsc/test/temba-utils-index.test.js +1178 -0
  72. package/out-tsc/test/temba-utils-index.test.js.map +1 -0
  73. package/out-tsc/test/temba-webchat-lightbox-fix.test.js +42 -0
  74. package/out-tsc/test/temba-webchat-lightbox-fix.test.js.map +1 -0
  75. package/out-tsc/test/temba-webchat.test.js +816 -0
  76. package/out-tsc/test/temba-webchat.test.js.map +1 -0
  77. package/out-tsc/test/utils.test.js +33 -1
  78. package/out-tsc/test/utils.test.js.map +1 -1
  79. package/package.json +6 -8
  80. package/screenshots/truth/alert/error.png +0 -0
  81. package/screenshots/truth/alert/info.png +0 -0
  82. package/screenshots/truth/alert/warning.png +0 -0
  83. package/screenshots/truth/checkbox/checkbox-label-background-hover.png +0 -0
  84. package/screenshots/truth/checkbox/checked.png +0 -0
  85. package/screenshots/truth/checkbox/default.png +0 -0
  86. package/screenshots/truth/colorpicker/default.png +0 -0
  87. package/screenshots/truth/colorpicker/focused.png +0 -0
  88. package/screenshots/truth/colorpicker/initialized.png +0 -0
  89. package/screenshots/truth/colorpicker/selected.png +0 -0
  90. package/screenshots/truth/compose/attachments-tab.png +0 -0
  91. package/screenshots/truth/compose/attachments-with-files-focused.png +0 -0
  92. package/screenshots/truth/compose/attachments-with-files.png +0 -0
  93. package/screenshots/truth/compose/intial-text.png +0 -0
  94. package/screenshots/truth/compose/no-counter.png +0 -0
  95. package/screenshots/truth/compose/wraps-text-and-spaces.png +0 -0
  96. package/screenshots/truth/compose/wraps-text-and-url.png +0 -0
  97. package/screenshots/truth/compose/wraps-text-no-spaces.png +0 -0
  98. package/screenshots/truth/contacts/badges.png +0 -0
  99. package/screenshots/truth/contacts/chat-failure.png +0 -0
  100. package/screenshots/truth/contacts/chat-for-active-contact.png +0 -0
  101. package/screenshots/truth/contacts/chat-for-archived-contact.png +0 -0
  102. package/screenshots/truth/contacts/chat-for-blocked-contact.png +0 -0
  103. package/screenshots/truth/contacts/chat-for-stopped-contact.png +0 -0
  104. package/screenshots/truth/contacts/chat-sends-attachments-only.png +0 -0
  105. package/screenshots/truth/contacts/chat-sends-text-and-attachments.png +0 -0
  106. package/screenshots/truth/contacts/chat-sends-text-only.png +0 -0
  107. package/screenshots/truth/content-menu/button-no-items.png +0 -0
  108. package/screenshots/truth/content-menu/items-and-buttons.png +0 -0
  109. package/screenshots/truth/counter/summary.png +0 -0
  110. package/screenshots/truth/counter/text.png +0 -0
  111. package/screenshots/truth/counter/unicode-variables.png +0 -0
  112. package/screenshots/truth/counter/unicode.png +0 -0
  113. package/screenshots/truth/counter/variable.png +0 -0
  114. package/screenshots/truth/date/date-inline.png +0 -0
  115. package/screenshots/truth/date/date.png +0 -0
  116. package/screenshots/truth/date/datetime.png +0 -0
  117. package/screenshots/truth/date/duration.png +0 -0
  118. package/screenshots/truth/date/timedate.png +0 -0
  119. package/screenshots/truth/datepicker/date-truncated-time.png +0 -0
  120. package/screenshots/truth/datepicker/date.png +0 -0
  121. package/screenshots/truth/datepicker/initial-timezone.png +0 -0
  122. package/screenshots/truth/datepicker/updated-keyboard-date.png +0 -0
  123. package/screenshots/truth/dialog/focused.png +0 -0
  124. package/screenshots/truth/dropdown/after-blur.png +0 -0
  125. package/screenshots/truth/dropdown/bottom-edge-collision.png +0 -0
  126. package/screenshots/truth/dropdown/custom-arrow-size.png +0 -0
  127. package/screenshots/truth/dropdown/default.png +0 -0
  128. package/screenshots/truth/dropdown/narrow-toggle.png +0 -0
  129. package/screenshots/truth/dropdown/no-mask.png +0 -0
  130. package/screenshots/truth/dropdown/opened.png +0 -0
  131. package/screenshots/truth/dropdown/positioned.png +0 -0
  132. package/screenshots/truth/dropdown/right-edge-collision.png +0 -0
  133. package/screenshots/truth/dropdown/with-mask.png +0 -0
  134. package/screenshots/truth/flow/editor-basic.png +0 -0
  135. package/screenshots/truth/label/custom.png +0 -0
  136. package/screenshots/truth/label/danger.png +0 -0
  137. package/screenshots/truth/label/dark.png +0 -0
  138. package/screenshots/truth/label/default-icon.png +0 -0
  139. package/screenshots/truth/label/no-icon.png +0 -0
  140. package/screenshots/truth/label/primary.png +0 -0
  141. package/screenshots/truth/label/secondary.png +0 -0
  142. package/screenshots/truth/label/shadow.png +0 -0
  143. package/screenshots/truth/label/tertiary.png +0 -0
  144. package/screenshots/truth/lightbox/img-zoomed.png +0 -0
  145. package/screenshots/truth/list/fields-dragging.png +0 -0
  146. package/screenshots/truth/list/fields-filtered.png +0 -0
  147. package/screenshots/truth/list/fields-hovered.png +0 -0
  148. package/screenshots/truth/list/fields.png +0 -0
  149. package/screenshots/truth/list/items-selected.png +0 -0
  150. package/screenshots/truth/list/items-updated.png +0 -0
  151. package/screenshots/truth/list/items.png +0 -0
  152. package/screenshots/truth/list/sortable-dragging.png +0 -0
  153. package/screenshots/truth/list/sortable-dropped.png +0 -0
  154. package/screenshots/truth/list/sortable.png +0 -0
  155. package/screenshots/truth/menu/menu-focused-with items.png +0 -0
  156. package/screenshots/truth/menu/menu-refresh-1.png +0 -0
  157. package/screenshots/truth/menu/menu-refresh-2.png +0 -0
  158. package/screenshots/truth/menu/menu-root.png +0 -0
  159. package/screenshots/truth/menu/menu-submenu.png +0 -0
  160. package/screenshots/truth/menu/menu-tasks-nextup.png +0 -0
  161. package/screenshots/truth/menu/menu-tasks.png +0 -0
  162. package/screenshots/truth/modax/form.png +0 -0
  163. package/screenshots/truth/modax/simple.png +0 -0
  164. package/screenshots/truth/omnibox/selected.png +0 -0
  165. package/screenshots/truth/options/block.png +0 -0
  166. package/screenshots/truth/run-list/basic.png +0 -0
  167. package/screenshots/truth/select/disabled-multi-selection.png +0 -0
  168. package/screenshots/truth/select/disabled-selection.png +0 -0
  169. package/screenshots/truth/select/disabled.png +0 -0
  170. package/screenshots/truth/select/embedded.png +0 -0
  171. package/screenshots/truth/select/empty-options.png +0 -0
  172. package/screenshots/truth/select/expression-selected.png +0 -0
  173. package/screenshots/truth/select/expressions.png +0 -0
  174. package/screenshots/truth/select/functions.png +0 -0
  175. package/screenshots/truth/select/local-options.png +0 -0
  176. package/screenshots/truth/select/multi-reorder-final.png +0 -0
  177. package/screenshots/truth/select/multi-reorder-initial.png +0 -0
  178. package/screenshots/truth/select/multi-with-endpoint.png +0 -0
  179. package/screenshots/truth/select/multiple-initial-values.png +0 -0
  180. package/screenshots/truth/select/remote-options.png +0 -0
  181. package/screenshots/truth/select/search-enabled.png +0 -0
  182. package/screenshots/truth/select/search-multi-no-matches.png +0 -0
  183. package/screenshots/truth/select/search-selected-focus.png +0 -0
  184. package/screenshots/truth/select/search-selected.png +0 -0
  185. package/screenshots/truth/select/search-with-selected.png +0 -0
  186. package/screenshots/truth/select/searching.png +0 -0
  187. package/screenshots/truth/select/selected-multi-maxitems-reached.png +0 -0
  188. package/screenshots/truth/select/selected-multi.png +0 -0
  189. package/screenshots/truth/select/selected-single.png +0 -0
  190. package/screenshots/truth/select/selection-clearable.png +0 -0
  191. package/screenshots/truth/select/static-initial-value.png +0 -0
  192. package/screenshots/truth/select/static-initial-via-selected.png +0 -0
  193. package/screenshots/truth/select/truncated-selection.png +0 -0
  194. package/screenshots/truth/select/with-placeholder.png +0 -0
  195. package/screenshots/truth/select/without-placeholder.png +0 -0
  196. package/screenshots/truth/slider/custom-min-custom-max-valid-value.png +0 -0
  197. package/screenshots/truth/slider/custom-min-default-max-no-value.png +0 -0
  198. package/screenshots/truth/slider/default-min-custom-max-no-value.png +0 -0
  199. package/screenshots/truth/slider/default-min-default-max-invalid-value.png +0 -0
  200. package/screenshots/truth/slider/default-min-default-max-valid-value.png +0 -0
  201. package/screenshots/truth/slider/update-slider-on-value-change.png +0 -0
  202. package/screenshots/truth/templates/default.png +0 -0
  203. package/screenshots/truth/templates/unapproved.png +0 -0
  204. package/screenshots/truth/textinput/input-disabled.png +0 -0
  205. package/screenshots/truth/textinput/input-focused.png +0 -0
  206. package/screenshots/truth/textinput/input-form.png +0 -0
  207. package/screenshots/truth/textinput/input-inserted.png +0 -0
  208. package/screenshots/truth/textinput/input-placeholder.png +0 -0
  209. package/screenshots/truth/textinput/input-updated.png +0 -0
  210. package/screenshots/truth/textinput/input.png +0 -0
  211. package/screenshots/truth/textinput/textarea-focused.png +0 -0
  212. package/screenshots/truth/textinput/textarea.png +0 -0
  213. package/screenshots/truth/tip/bottom.png +0 -0
  214. package/screenshots/truth/tip/left.png +0 -0
  215. package/screenshots/truth/tip/right.png +0 -0
  216. package/screenshots/truth/tip/top.png +0 -0
  217. package/screenshots/truth/webchat/closed-widget.png +0 -0
  218. package/screenshots/truth/webchat/connected-state.png +0 -0
  219. package/screenshots/truth/webchat/connecting-state.png +0 -0
  220. package/screenshots/truth/webchat/disconnected-state.png +0 -0
  221. package/screenshots/truth/webchat/opened-widget.png +0 -0
  222. package/src/chart/TembaChart.ts +86 -15
  223. package/src/fields/FieldManager.ts +30 -38
  224. package/src/list/RunList.ts +11 -8
  225. package/src/list/SortableList.ts +291 -67
  226. package/src/locales/es.ts +1 -0
  227. package/src/locales/fr.ts +1 -0
  228. package/src/locales/pt.ts +1 -0
  229. package/src/omnibox/Omnibox.ts +1 -1
  230. package/src/options/Options.ts +38 -13
  231. package/src/select/Select.ts +245 -47
  232. package/src/store/AppState.ts +3 -3
  233. package/src/utils/index.ts +17 -5
  234. package/src/vectoricon/VectorIcon.ts +2 -1
  235. package/src/webchat/WebChat.ts +5 -2
  236. package/temba-modules.ts +0 -2
  237. package/test/temba-appstate-language.test.ts +218 -0
  238. package/test/temba-chart.test.ts +161 -1
  239. package/test/temba-dropdown.test.ts +444 -0
  240. package/test/temba-flow-editor-node.test.ts +344 -0
  241. package/test/temba-flow-editor.test.ts +301 -0
  242. package/test/temba-flow-plumber.test.ts +189 -0
  243. package/test/temba-flow-render.test.ts +220 -0
  244. package/test/temba-omnibox.test.ts +2 -3
  245. package/test/temba-run-list.test.ts +774 -0
  246. package/test/temba-select.test.ts +206 -78
  247. package/test/temba-sortable-list.test.ts +108 -15
  248. package/test/temba-toast.test.ts +386 -0
  249. package/test/temba-utils-index.test.ts +1547 -0
  250. package/test/temba-webchat-lightbox-fix.test.ts +57 -0
  251. package/test/temba-webchat.test.ts +1095 -0
  252. package/test/utils.test.ts +56 -2
  253. package/test-assets/list/flow-results.json +17 -0
  254. package/test-assets/list/runs.json +126 -0
  255. package/test-assets/style.css +23 -0
  256. package/web-test-runner.config.mjs +33 -7
  257. package/xliff/es.xlf +3 -0
  258. package/xliff/fr.xlf +3 -0
  259. package/xliff/pt.xlf +3 -0
  260. package/out-tsc/src/outboxmonitor/OutboxMonitor.js +0 -136
  261. package/out-tsc/src/outboxmonitor/OutboxMonitor.js.map +0 -1
  262. package/src/outboxmonitor/OutboxMonitor.ts +0 -148
@@ -1,9 +1,10 @@
1
1
  import { __decorate } from "tslib";
2
2
  /* eslint-disable @typescript-eslint/no-empty-function */
3
3
  import { html, css } from 'lit';
4
- import { property } from 'lit/decorators.js';
4
+ import { property, state } from 'lit/decorators.js';
5
5
  import { getUrl, getClasses, fetchResults, postJSON } from '../utils';
6
6
  import '../options/Options';
7
+ import '../list/SortableList';
7
8
  import { FormElement } from '../FormElement';
8
9
  import { lru } from 'tiny-lru';
9
10
  import { CustomEventType } from '../interfaces';
@@ -52,7 +53,7 @@ export class Select extends FormElement {
52
53
  background: rgba(100, 100, 100, 0.05);
53
54
  }
54
55
 
55
- .selected-item.multi .remove-item {
56
+ . selected-item.multi .remove-item {
56
57
  display: none;
57
58
  }
58
59
 
@@ -88,7 +89,7 @@ export class Select extends FormElement {
88
89
  padding-top: 1px;
89
90
  box-shadow: var(--widget-box-shadow);
90
91
  position: relative;
91
- min-height: var(--temba-select-min-height, 2.5em);
92
+ min-height: var(--temba-select-min-height, 2.4em);
92
93
  }
93
94
 
94
95
  temba-icon.select-open:hover,
@@ -124,7 +125,7 @@ export class Select extends FormElement {
124
125
  flex-direction: row;
125
126
  align-items: stretch;
126
127
  user-select: none;
127
- padding: var(--temba-select-selected-padding);
128
+ padding: var(--temba-select-selected-padding, 0px 4px);
128
129
  }
129
130
 
130
131
  .searchable .selected {
@@ -183,6 +184,18 @@ export class Select extends FormElement {
183
184
  background: rgba(100, 100, 100, 0.3);
184
185
  }
185
186
 
187
+ .multi .selected-item.sortable {
188
+ cursor: move;
189
+ }
190
+
191
+ .multi .selected-item.dragging {
192
+ opacity: 0.5;
193
+ }
194
+
195
+ .multi temba-sortable-list {
196
+ margin: 0 !important;
197
+ }
198
+
186
199
  input {
187
200
  font-size: 13px;
188
201
  width: 0px;
@@ -201,16 +214,15 @@ export class Select extends FormElement {
201
214
  border: 0px solid purple !important;
202
215
  }
203
216
 
204
- .input-wrapper {
205
- min-width: 1px;
206
- }
207
-
208
217
  .input-wrapper:focus-within {
209
218
  min-width: 1px;
210
219
  }
211
220
 
212
221
  .input-wrapper {
222
+ min-width: 1px;
213
223
  margin-left: 6px;
224
+ margin-right: -6px;
225
+ display: flex;
214
226
  }
215
227
 
216
228
  .multi .input-wrapper {
@@ -250,9 +262,9 @@ export class Select extends FormElement {
250
262
  box-shadow: none !important;
251
263
  }
252
264
 
253
- .input-wrapper {
254
- display: flex;
255
- margin-right: 0em;
265
+ .multi .input-wrapper {
266
+ flex-shrink: 0;
267
+ min-width: 100px;
256
268
  }
257
269
 
258
270
  .input-wrapper .searchbox {
@@ -267,6 +279,7 @@ export class Select extends FormElement {
267
279
  color: var(--color-placeholder);
268
280
  display: none;
269
281
  line-height: var(--temba-select-selected-line-height);
282
+ margin-left: 6px;
270
283
  }
271
284
 
272
285
  .footer {
@@ -308,6 +321,10 @@ export class Select extends FormElement {
308
321
  pointer-events: none;
309
322
  padding: 0px;
310
323
  }
324
+
325
+ .ghost .remove-item {
326
+ display: none !important;
327
+ }
311
328
  `;
312
329
  }
313
330
  constructor() {
@@ -333,6 +350,7 @@ export class Select extends FormElement {
333
350
  this.cacheKey = '';
334
351
  this.focused = false;
335
352
  this.disabled = false;
353
+ this.attemptedOpen = false;
336
354
  this.selectedIndex = -1;
337
355
  this.anchorPosition = { left: 0, top: 0 };
338
356
  this.tags = false;
@@ -367,6 +385,7 @@ export class Select extends FormElement {
367
385
  this.renderSelectedItemDefault = this.renderSelectedItemDefault.bind(this);
368
386
  this.prepareOptionsDefault = this.prepareOptionsDefault.bind(this);
369
387
  this.isMatchDefault = this.isMatchDefault.bind(this);
388
+ this.handleOrderChanged = this.handleOrderChanged.bind(this);
370
389
  }
371
390
  prepareOptionsDefault(options) {
372
391
  return options;
@@ -651,6 +670,7 @@ export class Select extends FormElement {
651
670
  this.focused = false;
652
671
  }
653
672
  this.visibleOptions = [];
673
+ this.attemptedOpen = false;
654
674
  this.input = '';
655
675
  this.next = null;
656
676
  this.complete = true;
@@ -721,7 +741,7 @@ export class Select extends FormElement {
721
741
  this.shadowRoot.querySelector('.select-container').click();
722
742
  }
723
743
  isOpen() {
724
- return this.visibleOptions.length > 0;
744
+ return (this.visibleOptions.length > 0 || (this.attemptedOpen && this.focused));
725
745
  }
726
746
  setOptions(options) {
727
747
  this.staticOptions = options;
@@ -932,6 +952,7 @@ export class Select extends FormElement {
932
952
  }
933
953
  handleBlur() {
934
954
  this.focused = false;
955
+ this.attemptedOpen = false;
935
956
  if (this.visibleOptions.length > 0) {
936
957
  this.input = '';
937
958
  this.next = null;
@@ -988,7 +1009,9 @@ export class Select extends FormElement {
988
1009
  evt.key === 'ArrowDown' ||
989
1010
  (evt.key === 'n' && evt.ctrlKey)) {
990
1011
  if (this.visibleOptions.length === 0 &&
991
- this.completionOptions.length === 0) {
1012
+ this.completionOptions.length === 0 &&
1013
+ !this.input) {
1014
+ this.attemptedOpen = true;
992
1015
  this.requestUpdate('input');
993
1016
  return;
994
1017
  }
@@ -1021,13 +1044,18 @@ export class Select extends FormElement {
1021
1044
  }
1022
1045
  handleCancel() {
1023
1046
  this.visibleOptions = [];
1047
+ this.attemptedOpen = false;
1024
1048
  }
1025
1049
  handleCursorChanged(event) {
1026
1050
  this.cursorIndex = event.detail.index;
1027
1051
  }
1028
1052
  handleContainerClick(event) {
1029
- event.stopPropagation();
1030
- event.preventDefault();
1053
+ if (this.disabled) {
1054
+ // prevent opening dropdown right after drag-and-drop
1055
+ event.stopPropagation();
1056
+ event.preventDefault();
1057
+ return;
1058
+ }
1031
1059
  this.focused = true;
1032
1060
  if (event.target.tagName !== 'INPUT') {
1033
1061
  const input = this.shadowRoot.querySelector('input');
@@ -1036,11 +1064,16 @@ export class Select extends FormElement {
1036
1064
  input.focus();
1037
1065
  return;
1038
1066
  }
1039
- if (this.visibleOptions.length > 0) {
1067
+ // Check if dropdown is currently open (either with options or showing "No options")
1068
+ if (this.isOpen()) {
1040
1069
  this.visibleOptions = [];
1070
+ this.attemptedOpen = false;
1041
1071
  }
1042
1072
  else {
1073
+ this.attemptedOpen = true;
1043
1074
  this.requestUpdate('input');
1075
+ // Also trigger an immediate update to show empty dropdown
1076
+ this.requestUpdate();
1044
1077
  }
1045
1078
  }
1046
1079
  }
@@ -1056,6 +1089,9 @@ export class Select extends FormElement {
1056
1089
  ];
1057
1090
  }
1058
1091
  handleArrowClick(event) {
1092
+ if (this.disabled) {
1093
+ return;
1094
+ }
1059
1095
  if (this.isOpen()) {
1060
1096
  event.preventDefault();
1061
1097
  event.stopPropagation();
@@ -1069,7 +1105,16 @@ export class Select extends FormElement {
1069
1105
  // special case for icons on any option type
1070
1106
  const icon = option.icon;
1071
1107
  return html `
1072
- <div class="option-name" style="display:flex">
1108
+ <div
1109
+ class="option-name"
1110
+ style="flex: 1 1 auto;
1111
+ align-self: center;
1112
+ white-space: nowrap;
1113
+ overflow: hidden;
1114
+ text-overflow: ellipsis;
1115
+ padding: 2px 8px;
1116
+ display: flex;"
1117
+ >
1073
1118
  ${icon
1074
1119
  ? html `<temba-icon
1075
1120
  name="${icon}"
@@ -1127,6 +1172,17 @@ export class Select extends FormElement {
1127
1172
  const idx = this.values.indexOf(valueToRemove);
1128
1173
  if (idx > -1) {
1129
1174
  this.values.splice(idx, 1);
1175
+ // Also remove the 'selected' attribute from the corresponding temba-option element
1176
+ const valueToMatch = this.getValue(valueToRemove);
1177
+ for (const child of this.children) {
1178
+ if (child.tagName === 'TEMBA-OPTION') {
1179
+ const childValue = child.getAttribute('value');
1180
+ if (childValue === valueToMatch) {
1181
+ child.removeAttribute('selected');
1182
+ break;
1183
+ }
1184
+ }
1185
+ }
1130
1186
  }
1131
1187
  this.requestUpdate('values', oldValues);
1132
1188
  this.infoText = '';
@@ -1142,6 +1198,33 @@ export class Select extends FormElement {
1142
1198
  this.values = [];
1143
1199
  this.requestUpdate('values', oldValues);
1144
1200
  }
1201
+ shouldShowEmptyMessage() {
1202
+ return (this.attemptedOpen &&
1203
+ this.focused &&
1204
+ this.visibleOptions.length === 0 &&
1205
+ !this.input &&
1206
+ this.staticOptions.length === 0 &&
1207
+ !this.endpoint);
1208
+ }
1209
+ handleOrderChanged(event) {
1210
+ const detail = event.detail;
1211
+ // Handle new swap-based format
1212
+ if (detail.swap && Array.isArray(detail.swap) && detail.swap.length === 2) {
1213
+ const [fromIdx, toIdx] = detail.swap;
1214
+ // Only reorder if the indexes are different and valid
1215
+ if (fromIdx !== toIdx &&
1216
+ fromIdx >= 0 &&
1217
+ toIdx >= 0 &&
1218
+ fromIdx < this.values.length &&
1219
+ toIdx < this.values.length) {
1220
+ const oldValues = [...this.values];
1221
+ // Move the item from fromIdx to toIdx
1222
+ const movedItem = this.values.splice(fromIdx, 1)[0];
1223
+ this.values.splice(toIdx, 0, movedItem);
1224
+ this.requestUpdate('values', oldValues);
1225
+ }
1226
+ }
1227
+ }
1145
1228
  render() {
1146
1229
  const placeholder = this.values.length === 0 ? this.placeholder : '';
1147
1230
  const placeholderDiv = html `
@@ -1218,33 +1301,126 @@ export class Select extends FormElement {
1218
1301
  ></temba-loading>`
1219
1302
  : null}
1220
1303
  ${!this.multi && !this.resolving ? input : null}
1221
- ${this.values.map((selected, index) => html `
1222
- <div
1223
- class="selected-item ${index === this.selectedIndex
1224
- ? 'focused'
1225
- : ''}"
1226
- >
1227
- ${this.multi
1304
+ ${this.multi && this.values.length > 1
1228
1305
  ? html `
1229
- <div
1230
- class="remove-item"
1231
- style="margin-top:1px"
1232
- @click=${(evt) => {
1233
- evt.preventDefault();
1234
- evt.stopPropagation();
1235
- this.handleRemoveSelection(selected);
1306
+ <temba-sortable-list
1307
+ horizontal
1308
+ @temba-order-changed=${this.handleOrderChanged}
1309
+ .prepareGhost=${(item) => {
1310
+ item.style.transform = 'scale(1)';
1311
+ item.querySelector('.remove-item').style.display =
1312
+ 'none';
1236
1313
  }}
1237
- >
1238
- <temba-icon
1239
- name="${Icon.delete_small}"
1240
- size="1"
1241
- ></temba-icon>
1242
- </div>
1243
- `
1244
- : null}
1245
- ${this.renderSelectedItem(selected)}
1246
- </div>
1247
- `)}
1314
+ >
1315
+ ${this.values.map((selected, index) => html `
1316
+ <div
1317
+ class="selected-item sortable ${index ===
1318
+ this.selectedIndex
1319
+ ? 'focused'
1320
+ : ''} ${this.draggingId === `selected-${index}`
1321
+ ? 'dragging'
1322
+ : ''}"
1323
+ id="selected-${index}"
1324
+ style="
1325
+ vertical-align: middle;
1326
+ background: rgba(100,100,100,0.1);
1327
+ user-select: none;
1328
+ border-radius: 2px;
1329
+ align-items: center;
1330
+ flex-direction: row;
1331
+ flex-wrap: nowrap;
1332
+ margin: 2px 2px;
1333
+ display: flex;
1334
+ overflow: hidden;
1335
+ color: var(--color-widget-text);
1336
+ line-height: var(--temba-select-selected-line-height);
1337
+ --icon-color: var(--color-text-dark);
1338
+ ${index === this.selectedIndex
1339
+ ? 'background: rgba(100,100,100,0.3);'
1340
+ : ''}
1341
+ ${this.draggingId === `selected-${index}`
1342
+ ? 'opacity: 0.5;'
1343
+ : ''}
1344
+ "
1345
+ >
1346
+ ${this.multi
1347
+ ? html `
1348
+ <div
1349
+ class="remove-item"
1350
+ style="
1351
+ cursor: pointer;
1352
+ display: inline-block;
1353
+ padding: 3px 6px;
1354
+ border-right: 1px solid rgba(100,100,100,0.2);
1355
+ margin: 0;
1356
+ background: rgba(100,100,100,0.05);
1357
+ margin-top:1px;
1358
+ "
1359
+ @click=${(evt) => {
1360
+ evt.preventDefault();
1361
+ evt.stopPropagation();
1362
+ this.handleRemoveSelection(selected);
1363
+ }}
1364
+ >
1365
+ <temba-icon
1366
+ name="${Icon.delete_small}"
1367
+ size="1"
1368
+ ></temba-icon>
1369
+ </div>
1370
+ `
1371
+ : null}
1372
+ ${this.renderSelectedItem(selected)}
1373
+ </div>
1374
+ `)}
1375
+ </temba-sortable-list>
1376
+ `
1377
+ : this.values.map((selected, index) => html `
1378
+ <div
1379
+ class="selected-item ${index === this.selectedIndex
1380
+ ? 'focused'
1381
+ : ''}"
1382
+ style="
1383
+ display: flex;
1384
+ overflow: hidden;
1385
+ color: var(--color-widget-text);
1386
+ line-height: var(--temba-select-selected-line-height);
1387
+ --icon-color: var(--color-text-dark);
1388
+ ${index === this.selectedIndex
1389
+ ? 'background: rgba(100,100,100,0.3);'
1390
+ : ''}
1391
+ "
1392
+ >
1393
+ ${this.multi
1394
+ ? html `
1395
+ <div
1396
+ class="remove-item"
1397
+ style="
1398
+ cursor: pointer;
1399
+ display: inline-block;
1400
+ padding: 3px 6px;
1401
+ border-right: 1px solid rgba(100,100,100,0.2);
1402
+ margin: 0;
1403
+ background: rgba(100,100,100,0.05);
1404
+ margin-top:1px;
1405
+ "
1406
+ @click=${(evt) => {
1407
+ evt.preventDefault();
1408
+ evt.stopPropagation();
1409
+ this.handleRemoveSelection(selected);
1410
+ }}
1411
+ >
1412
+ <temba-icon
1413
+ name="${Icon.delete_small}"
1414
+ size="1"
1415
+ ></temba-icon>
1416
+ </div>
1417
+ `
1418
+ : null}
1419
+ ${!this.input
1420
+ ? this.renderSelectedItem(selected)
1421
+ : null}
1422
+ </div>
1423
+ `)}
1248
1424
  ${this.multi ? input : null}
1249
1425
  </div>
1250
1426
 
@@ -1287,7 +1463,8 @@ export class Select extends FormElement {
1287
1463
  .getName=${this.getNameInternal}
1288
1464
  ?static-width=${this.optionWidth}
1289
1465
  ?anchor-right=${this.anchorRight}
1290
- ?visible=${this.visibleOptions.length > 0}
1466
+ ?visible=${this.visibleOptions.length > 0 || this.shouldShowEmptyMessage()}
1467
+ ?showEmptyMessage=${this.shouldShowEmptyMessage()}
1291
1468
  ></temba-options>
1292
1469
 
1293
1470
  <temba-options
@@ -1390,6 +1567,9 @@ __decorate([
1390
1567
  __decorate([
1391
1568
  property({ type: Boolean })
1392
1569
  ], Select.prototype, "disabled", void 0);
1570
+ __decorate([
1571
+ state()
1572
+ ], Select.prototype, "attemptedOpen", void 0);
1393
1573
  __decorate([
1394
1574
  property({ attribute: false })
1395
1575
  ], Select.prototype, "selectedIndex", void 0);
@@ -1486,4 +1666,7 @@ __decorate([
1486
1666
  __decorate([
1487
1667
  property({ type: Boolean })
1488
1668
  ], Select.prototype, "allowAnchor", void 0);
1669
+ __decorate([
1670
+ property({ type: String })
1671
+ ], Select.prototype, "draggingId", void 0);
1489
1672
  //# sourceMappingURL=Select.js.map