@nyaruka/temba-components 0.129.3 → 0.129.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.
Files changed (304) hide show
  1. package/.eslintrc.js +1 -0
  2. package/.github/workflows/build.yml +135 -3
  3. package/CHANGELOG.md +19 -0
  4. package/demo/data/flows/sample-flow.json +110 -87
  5. package/demo/field-config-demo.html +135 -0
  6. package/dist/temba-components.js +1257 -675
  7. package/dist/temba-components.js.map +1 -1
  8. package/docs/ActionEditor-Migration.md +118 -0
  9. package/out-tsc/src/events.js.map +1 -1
  10. package/out-tsc/src/flow/{EditorNode.js → CanvasNode.js} +345 -42
  11. package/out-tsc/src/flow/CanvasNode.js.map +1 -0
  12. package/out-tsc/src/flow/Editor.js +107 -3
  13. package/out-tsc/src/flow/Editor.js.map +1 -1
  14. package/out-tsc/src/flow/NodeEditor.js +1211 -0
  15. package/out-tsc/src/flow/NodeEditor.js.map +1 -0
  16. package/out-tsc/src/flow/Plumber.js +0 -6
  17. package/out-tsc/src/flow/Plumber.js.map +1 -1
  18. package/out-tsc/src/flow/actions/add_contact_groups.js +40 -0
  19. package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -0
  20. package/out-tsc/src/flow/actions/add_contact_urn.js +16 -0
  21. package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -0
  22. package/out-tsc/src/flow/actions/add_input_labels.js +11 -0
  23. package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -0
  24. package/out-tsc/src/flow/actions/call_classifier.js +11 -0
  25. package/out-tsc/src/flow/actions/call_classifier.js.map +1 -0
  26. package/out-tsc/src/flow/actions/call_llm.js +11 -0
  27. package/out-tsc/src/flow/actions/call_llm.js.map +1 -0
  28. package/out-tsc/src/flow/actions/call_resthook.js +11 -0
  29. package/out-tsc/src/flow/actions/call_resthook.js.map +1 -0
  30. package/out-tsc/src/flow/actions/call_webhook.js +122 -0
  31. package/out-tsc/src/flow/actions/call_webhook.js.map +1 -0
  32. package/out-tsc/src/flow/actions/enter_flow.js +14 -0
  33. package/out-tsc/src/flow/actions/enter_flow.js.map +1 -0
  34. package/out-tsc/src/flow/actions/open_ticket.js +11 -0
  35. package/out-tsc/src/flow/actions/open_ticket.js.map +1 -0
  36. package/out-tsc/src/flow/actions/play_audio.js +11 -0
  37. package/out-tsc/src/flow/actions/play_audio.js.map +1 -0
  38. package/out-tsc/src/flow/actions/remove_contact_groups.js +62 -0
  39. package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -0
  40. package/out-tsc/src/flow/actions/request_optin.js +11 -0
  41. package/out-tsc/src/flow/actions/request_optin.js.map +1 -0
  42. package/out-tsc/src/flow/actions/say_msg.js +11 -0
  43. package/out-tsc/src/flow/actions/say_msg.js.map +1 -0
  44. package/out-tsc/src/flow/actions/send_broadcast.js +33 -0
  45. package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -0
  46. package/out-tsc/src/flow/actions/send_email.js +56 -0
  47. package/out-tsc/src/flow/actions/send_email.js.map +1 -0
  48. package/out-tsc/src/flow/actions/send_msg.js +55 -0
  49. package/out-tsc/src/flow/actions/send_msg.js.map +1 -0
  50. package/out-tsc/src/flow/actions/set_contact_channel.js +12 -0
  51. package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -0
  52. package/out-tsc/src/flow/actions/set_contact_field.js +12 -0
  53. package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -0
  54. package/out-tsc/src/flow/actions/set_contact_language.js +10 -0
  55. package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -0
  56. package/out-tsc/src/flow/actions/set_contact_name.js +10 -0
  57. package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -0
  58. package/out-tsc/src/flow/actions/set_contact_status.js +10 -0
  59. package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -0
  60. package/out-tsc/src/flow/actions/set_run_result.js +10 -0
  61. package/out-tsc/src/flow/actions/set_run_result.js.map +1 -0
  62. package/out-tsc/src/flow/actions/split_by_expression_example.js +77 -0
  63. package/out-tsc/src/flow/actions/split_by_expression_example.js.map +1 -0
  64. package/out-tsc/src/flow/actions/start_session.js +11 -0
  65. package/out-tsc/src/flow/actions/start_session.js.map +1 -0
  66. package/out-tsc/src/flow/actions/transfer_airtime.js +11 -0
  67. package/out-tsc/src/flow/actions/transfer_airtime.js.map +1 -0
  68. package/out-tsc/src/flow/config.js +88 -193
  69. package/out-tsc/src/flow/config.js.map +1 -1
  70. package/out-tsc/src/flow/nodes/execute_actions.js +4 -0
  71. package/out-tsc/src/flow/nodes/execute_actions.js.map +1 -0
  72. package/out-tsc/src/flow/nodes/split_by_airtime.js +9 -0
  73. package/out-tsc/src/flow/nodes/split_by_airtime.js.map +1 -0
  74. package/out-tsc/src/flow/nodes/split_by_contact_field.js +7 -0
  75. package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -0
  76. package/out-tsc/src/flow/nodes/split_by_expression.js +7 -0
  77. package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -0
  78. package/out-tsc/src/flow/nodes/split_by_groups.js +7 -0
  79. package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -0
  80. package/out-tsc/src/flow/nodes/split_by_random.js +10 -0
  81. package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -0
  82. package/out-tsc/src/flow/nodes/split_by_run_result.js +7 -0
  83. package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -0
  84. package/out-tsc/src/flow/nodes/split_by_scheme.js +7 -0
  85. package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -0
  86. package/out-tsc/src/flow/nodes/split_by_subflow.js +9 -0
  87. package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -0
  88. package/out-tsc/src/flow/nodes/split_by_webhook.js +18 -0
  89. package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -0
  90. package/out-tsc/src/flow/nodes/wait_for_audio.js +7 -0
  91. package/out-tsc/src/flow/nodes/wait_for_audio.js.map +1 -0
  92. package/out-tsc/src/flow/nodes/wait_for_digits.js +7 -0
  93. package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -0
  94. package/out-tsc/src/flow/nodes/wait_for_image.js +7 -0
  95. package/out-tsc/src/flow/nodes/wait_for_image.js.map +1 -0
  96. package/out-tsc/src/flow/nodes/wait_for_location.js +7 -0
  97. package/out-tsc/src/flow/nodes/wait_for_location.js.map +1 -0
  98. package/out-tsc/src/flow/nodes/wait_for_menu.js +7 -0
  99. package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -0
  100. package/out-tsc/src/flow/nodes/wait_for_response.js +7 -0
  101. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -0
  102. package/out-tsc/src/flow/nodes/wait_for_video.js +7 -0
  103. package/out-tsc/src/flow/nodes/wait_for_video.js.map +1 -0
  104. package/out-tsc/src/flow/types.js +79 -0
  105. package/out-tsc/src/flow/types.js.map +1 -0
  106. package/out-tsc/src/flow/utils.js +65 -0
  107. package/out-tsc/src/flow/utils.js.map +1 -0
  108. package/out-tsc/src/form/ArrayEditor.js +199 -0
  109. package/out-tsc/src/form/ArrayEditor.js.map +1 -0
  110. package/out-tsc/src/form/BaseListEditor.js +128 -0
  111. package/out-tsc/src/form/BaseListEditor.js.map +1 -0
  112. package/out-tsc/src/form/Checkbox.js +17 -2
  113. package/out-tsc/src/form/Checkbox.js.map +1 -1
  114. package/out-tsc/src/form/Completion.js +6 -0
  115. package/out-tsc/src/form/Completion.js.map +1 -1
  116. package/out-tsc/src/form/FormField.js +110 -11
  117. package/out-tsc/src/form/FormField.js.map +1 -1
  118. package/out-tsc/src/form/KeyValueEditor.js +223 -0
  119. package/out-tsc/src/form/KeyValueEditor.js.map +1 -0
  120. package/out-tsc/src/form/select/Select.js +92 -32
  121. package/out-tsc/src/form/select/Select.js.map +1 -1
  122. package/out-tsc/src/interfaces.js +6 -0
  123. package/out-tsc/src/interfaces.js.map +1 -1
  124. package/out-tsc/src/live/ContactChat.js +2 -76
  125. package/out-tsc/src/live/ContactChat.js.map +1 -1
  126. package/out-tsc/temba-modules.js +9 -2
  127. package/out-tsc/temba-modules.js.map +1 -1
  128. package/out-tsc/test/ActionHelper.js +116 -0
  129. package/out-tsc/test/ActionHelper.js.map +1 -0
  130. package/out-tsc/test/actions/add_contact_groups.test.js +66 -0
  131. package/out-tsc/test/actions/add_contact_groups.test.js.map +1 -0
  132. package/out-tsc/test/actions/remove_contact_groups.test.js +226 -0
  133. package/out-tsc/test/actions/remove_contact_groups.test.js.map +1 -0
  134. package/out-tsc/test/actions/send_email.test.js +160 -0
  135. package/out-tsc/test/actions/send_email.test.js.map +1 -0
  136. package/out-tsc/test/actions/send_msg.test.js +95 -0
  137. package/out-tsc/test/actions/send_msg.test.js.map +1 -0
  138. package/out-tsc/test/temba-action-editing-integration.test.js +183 -0
  139. package/out-tsc/test/temba-action-editing-integration.test.js.map +1 -0
  140. package/out-tsc/test/temba-checkbox.test.js +1 -1
  141. package/out-tsc/test/temba-checkbox.test.js.map +1 -1
  142. package/out-tsc/test/temba-field-config.test.js +133 -0
  143. package/out-tsc/test/temba-field-config.test.js.map +1 -0
  144. package/out-tsc/test/temba-flow-editor-node.test.js +14 -14
  145. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
  146. package/out-tsc/test/temba-node-editor.test.js +283 -0
  147. package/out-tsc/test/temba-node-editor.test.js.map +1 -0
  148. package/out-tsc/test/temba-select.test.js +158 -0
  149. package/out-tsc/test/temba-select.test.js.map +1 -1
  150. package/package.json +1 -1
  151. package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
  152. package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
  153. package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
  154. package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
  155. package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
  156. package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
  157. package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
  158. package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
  159. package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
  160. package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
  161. package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
  162. package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
  163. package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
  164. package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
  165. package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
  166. package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
  167. package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
  168. package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
  169. package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
  170. package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
  171. package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
  172. package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
  173. package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
  174. package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
  175. package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
  176. package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
  177. package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
  178. package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
  179. package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
  180. package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
  181. package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
  182. package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
  183. package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
  184. package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
  185. package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
  186. package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
  187. package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
  188. package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
  189. package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
  190. package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
  191. package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
  192. package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
  193. package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
  194. package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
  195. package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
  196. package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
  197. package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
  198. package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
  199. package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
  200. package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
  201. package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
  202. package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
  203. package/screenshots/truth/editor/router.png +0 -0
  204. package/screenshots/truth/editor/send_msg.png +0 -0
  205. package/screenshots/truth/editor/set_contact_language.png +0 -0
  206. package/screenshots/truth/editor/set_contact_name.png +0 -0
  207. package/screenshots/truth/editor/set_run_result.png +0 -0
  208. package/screenshots/truth/editor/wait.png +0 -0
  209. package/screenshots/truth/formfield/markdown-errors.png +0 -0
  210. package/screenshots/truth/formfield/plain-text-errors.png +0 -0
  211. package/screenshots/truth/formfield/widget-only-markdown-errors.png +0 -0
  212. package/screenshots/truth/integration/checkbox-markdown-errors.png +0 -0
  213. package/src/events.ts +1 -40
  214. package/src/flow/{EditorNode.ts → CanvasNode.ts} +424 -48
  215. package/src/flow/Editor.ts +140 -4
  216. package/src/flow/NodeEditor.ts +1454 -0
  217. package/src/flow/Plumber.ts +0 -9
  218. package/src/flow/actions/add_contact_groups.ts +42 -0
  219. package/src/flow/actions/add_contact_urn.ts +17 -0
  220. package/src/flow/actions/add_input_labels.ts +12 -0
  221. package/src/flow/actions/call_classifier.ts +12 -0
  222. package/src/flow/actions/call_llm.ts +12 -0
  223. package/src/flow/actions/call_resthook.ts +12 -0
  224. package/src/flow/actions/call_webhook.ts +133 -0
  225. package/src/flow/actions/enter_flow.ts +15 -0
  226. package/src/flow/actions/open_ticket.ts +12 -0
  227. package/src/flow/actions/play_audio.ts +12 -0
  228. package/src/flow/actions/remove_contact_groups.ts +66 -0
  229. package/src/flow/actions/request_optin.ts +12 -0
  230. package/src/flow/actions/say_msg.ts +12 -0
  231. package/src/flow/actions/send_broadcast.ts +35 -0
  232. package/src/flow/actions/send_email.ts +60 -0
  233. package/src/flow/actions/send_msg.ts +58 -0
  234. package/src/flow/actions/set_contact_channel.ts +13 -0
  235. package/src/flow/actions/set_contact_field.ts +13 -0
  236. package/src/flow/actions/set_contact_language.ts +11 -0
  237. package/src/flow/actions/set_contact_name.ts +11 -0
  238. package/src/flow/actions/set_contact_status.ts +11 -0
  239. package/src/flow/actions/set_run_result.ts +11 -0
  240. package/src/flow/actions/split_by_expression_example.ts +88 -0
  241. package/src/flow/actions/start_session.ts +12 -0
  242. package/src/flow/actions/transfer_airtime.ts +12 -0
  243. package/src/flow/config.ts +93 -232
  244. package/src/flow/nodes/execute_actions.ts +5 -0
  245. package/src/flow/nodes/split_by_airtime.ts +9 -0
  246. package/src/flow/nodes/split_by_contact_field.ts +7 -0
  247. package/src/flow/nodes/split_by_expression.ts +7 -0
  248. package/src/flow/nodes/split_by_groups.ts +7 -0
  249. package/src/flow/nodes/split_by_random.ts +10 -0
  250. package/src/flow/nodes/split_by_run_result.ts +7 -0
  251. package/src/flow/nodes/split_by_scheme.ts +7 -0
  252. package/src/flow/nodes/split_by_subflow.ts +9 -0
  253. package/src/flow/nodes/split_by_webhook.ts +19 -0
  254. package/src/flow/nodes/wait_for_audio.ts +7 -0
  255. package/src/flow/nodes/wait_for_digits.ts +7 -0
  256. package/src/flow/nodes/wait_for_image.ts +7 -0
  257. package/src/flow/nodes/wait_for_location.ts +7 -0
  258. package/src/flow/nodes/wait_for_menu.ts +7 -0
  259. package/src/flow/nodes/wait_for_response.ts +7 -0
  260. package/src/flow/nodes/wait_for_video.ts +7 -0
  261. package/src/flow/types.ts +352 -0
  262. package/src/flow/utils.ts +76 -0
  263. package/src/form/ArrayEditor.ts +240 -0
  264. package/src/form/BaseListEditor.ts +177 -0
  265. package/src/form/Checkbox.ts +22 -3
  266. package/src/form/Completion.ts +6 -0
  267. package/src/form/FormField.ts +115 -11
  268. package/src/form/KeyValueEditor.ts +251 -0
  269. package/src/form/select/Select.ts +105 -32
  270. package/src/interfaces.ts +7 -2
  271. package/src/live/ContactChat.ts +3 -97
  272. package/src/store/flow-definition.d.ts +6 -1
  273. package/static/api/contacts.json +30 -0
  274. package/static/api/groups.json +4 -426
  275. package/static/api/locations.json +24 -0
  276. package/static/api/media.json +5 -0
  277. package/static/api/optins.json +16 -0
  278. package/static/api/orgs.json +13 -0
  279. package/static/api/topics.json +21 -0
  280. package/static/api/users.json +26 -0
  281. package/static/css/temba-components.css +3 -6
  282. package/temba-modules.ts +9 -2
  283. package/test/ActionHelper.ts +142 -0
  284. package/test/actions/add_contact_groups.test.ts +89 -0
  285. package/test/actions/remove_contact_groups.test.ts +265 -0
  286. package/test/actions/send_email.test.ts +214 -0
  287. package/test/actions/send_msg.test.ts +130 -0
  288. package/test/temba-action-editing-integration.test.ts +240 -0
  289. package/test/temba-checkbox.test.ts +1 -1
  290. package/test/temba-field-config.test.ts +152 -0
  291. package/test/temba-flow-editor-node.test.ts +18 -18
  292. package/test/temba-node-editor.test.ts +353 -0
  293. package/test/temba-select.test.ts +234 -0
  294. package/test-assets/contacts/history.json +11 -33
  295. package/web-dev-server.config.mjs +34 -0
  296. package/.github/workflows/coverage.yml +0 -80
  297. package/demo/sticky-note-demo.html +0 -155
  298. package/out-tsc/src/flow/EditorNode.js.map +0 -1
  299. package/out-tsc/src/flow/render.js +0 -358
  300. package/out-tsc/src/flow/render.js.map +0 -1
  301. package/out-tsc/test/temba-flow-render.test.js +0 -794
  302. package/out-tsc/test/temba-flow-render.test.js.map +0 -1
  303. package/src/flow/render.ts +0 -443
  304. package/test/temba-flow-render.test.ts +0 -1003
@@ -31,6 +31,10 @@ export class Select extends FormElement {
31
31
  --temba-options-font-size: var(--temba-select-selected-font-size);
32
32
  --icon-color: var(--color-text-dark);
33
33
  --color-options-bg: #fff;
34
+ /* Always use normal border colors for options popup, even when select is in error state */
35
+ --color-widget-border: #ddd;
36
+ --color-focus: #007bff;
37
+ --widget-box-shadow-focused: 0 0 0 3px rgba(0, 123, 255, 0.25);
34
38
  }
35
39
 
36
40
  :host:focus {
@@ -354,6 +358,7 @@ export class Select extends FormElement {
354
358
  this.selectedIndex = -1;
355
359
  this.anchorPosition = { left: 0, top: 0 };
356
360
  this.tags = false;
361
+ this.emails = false;
357
362
  this.flavor = 'default';
358
363
  this.infoText = '';
359
364
  this.values = [];
@@ -419,7 +424,7 @@ export class Select extends FormElement {
419
424
  }
420
425
  this.staticOptions.push(option);
421
426
  if (selected) {
422
- if (this.multi) {
427
+ if (this.isMultiMode) {
423
428
  this.addValue(option);
424
429
  }
425
430
  else {
@@ -537,9 +542,11 @@ export class Select extends FormElement {
537
542
  }
538
543
  }
539
544
  // remove any arbitrary values
540
- this.values = this.values.filter((value) => {
541
- return !value.arbitrary;
542
- });
545
+ for (let i = this.values.length - 1; i >= 0; i--) {
546
+ if (this.values[i].arbitrary) {
547
+ this.values.splice(i, 1);
548
+ }
549
+ }
543
550
  // reset our cache
544
551
  this.cacheKey = new Date().getTime().toString();
545
552
  this.fireEvent('change');
@@ -639,9 +646,13 @@ export class Select extends FormElement {
639
646
  else {
640
647
  const name = this.getAttribute('name');
641
648
  if (name) {
642
- if (!this.multi && this.values.length === 1) {
649
+ if (!this.isMultiMode && this.values.length === 1) {
643
650
  this.selection = this.values[0];
644
651
  this.value = this.serializeValue(this.values[0]);
652
+ // Ensure FormElement internals are updated
653
+ if (this.internals) {
654
+ this.internals.setFormValue(this.value);
655
+ }
645
656
  }
646
657
  else {
647
658
  if (this.inputRoot.parentElement) {
@@ -659,13 +670,13 @@ export class Select extends FormElement {
659
670
  }
660
671
  }
661
672
  setSelectedOption(option) {
662
- if (this.multi) {
673
+ if (this.isMultiMode) {
663
674
  this.addValue(option);
664
675
  }
665
676
  else {
666
677
  this.setValues([option]);
667
678
  }
668
- if (!this.multi || !this.searchable) {
679
+ if (!this.isMultiMode || !this.searchable) {
669
680
  this.blur();
670
681
  this.focused = false;
671
682
  }
@@ -682,7 +693,7 @@ export class Select extends FormElement {
682
693
  (this.cursorIndex || 0) > this.visibleOptions.length - LOOK_AHEAD);
683
694
  }
684
695
  handleOptionSelection(event) {
685
- if (this.multi &&
696
+ if (this.isMultiMode &&
686
697
  this.maxItems > 0 &&
687
698
  this.values.length >= this.maxItems) {
688
699
  this.infoText = this.maxItemsText;
@@ -734,9 +745,24 @@ export class Select extends FormElement {
734
745
  this.removeValue(selectionToRemove);
735
746
  this.visibleOptions = [];
736
747
  }
737
- createArbitraryOptionDefault() {
748
+ createArbitraryOptionDefault(input, _options) {
749
+ if (this.emails && input && this.isValidEmail(input)) {
750
+ return { name: input, value: input };
751
+ }
752
+ if (this.tags && input) {
753
+ return { name: input, value: input };
754
+ }
738
755
  return null;
739
756
  }
757
+ isValidEmail(email) {
758
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
759
+ return emailRegex.test(email);
760
+ }
761
+ // Helper method to determine if this select should behave as multi-select
762
+ // Returns true if multi is explicitly set OR if emails mode is enabled
763
+ get isMultiMode() {
764
+ return this.multi || this.emails;
765
+ }
740
766
  open() {
741
767
  this.shadowRoot.querySelector('.select-container').click();
742
768
  }
@@ -788,7 +814,7 @@ export class Select extends FormElement {
788
814
  // filter out any options already selected by id
789
815
  // TODO: should maybe be doing a deep equals here with option to optimize
790
816
  if (this.values.length > 0) {
791
- if (this.multi) {
817
+ if (this.isMultiMode) {
792
818
  options = options.filter((option) => !this.values.find((selected) => this.getValue(selected) === this.getValue(option)));
793
819
  }
794
820
  else {
@@ -802,7 +828,7 @@ export class Select extends FormElement {
802
828
  this.requestUpdate('cursorIndex');
803
829
  }
804
830
  }
805
- if (this.multi &&
831
+ if (this.isMultiMode &&
806
832
  this.maxItems > 0 &&
807
833
  this.values.length >= this.maxItems) {
808
834
  options = [];
@@ -842,6 +868,12 @@ export class Select extends FormElement {
842
868
  options.splice(0, 0, { name: query, value: query });
843
869
  }
844
870
  }
871
+ if (this.emails && q) {
872
+ if (this.isValidEmail(q) &&
873
+ !options.find((option) => this.getValue(option) && this.getValue(option).toLowerCase() === q)) {
874
+ options.splice(0, 0, { name: query, value: query });
875
+ }
876
+ }
845
877
  if (this.endpoint) {
846
878
  let url = this.endpoint;
847
879
  if (next) {
@@ -868,7 +900,7 @@ export class Select extends FormElement {
868
900
  }
869
901
  }
870
902
  const cache = this.lruCache.get(url);
871
- if (this.cache && !this.tags && cache) {
903
+ if (this.cache && !this.tags && !this.emails && cache) {
872
904
  if (page === 0 && !this.next) {
873
905
  this.cursorIndex = 0;
874
906
  this.setVisibleOptions([...options, ...cache.options]);
@@ -885,7 +917,7 @@ export class Select extends FormElement {
885
917
  if (this.searchable && !this.queryParam) {
886
918
  fetchResults(url).then((results) => {
887
919
  results = this.prepareOptions(results);
888
- if (this.cache && !this.tags) {
920
+ if (this.cache && !this.tags && !this.emails) {
889
921
  this.lruCache.set(url, {
890
922
  options: results,
891
923
  complete: true,
@@ -922,7 +954,7 @@ export class Select extends FormElement {
922
954
  }
923
955
  this.complete = this.isComplete(results, response);
924
956
  }
925
- if (this.cache && !this.tags) {
957
+ if (this.cache && !this.tags && !this.emails) {
926
958
  this.lruCache.set(url, {
927
959
  options: results,
928
960
  complete: this.complete,
@@ -960,7 +992,7 @@ export class Select extends FormElement {
960
992
  this.visibleOptions = [];
961
993
  this.cursorIndex = 0;
962
994
  }
963
- if (this.multi &&
995
+ if (this.isMultiMode &&
964
996
  this.maxItems > 0 &&
965
997
  this.values.length >= this.maxItems) {
966
998
  this.infoText = '';
@@ -977,7 +1009,7 @@ export class Select extends FormElement {
977
1009
  value: ele.value,
978
1010
  expression: true
979
1011
  };
980
- if (this.multi) {
1012
+ if (this.isMultiMode) {
981
1013
  if (!this.values.find((option) => {
982
1014
  return (option.expression &&
983
1015
  option.value &&
@@ -992,7 +1024,7 @@ export class Select extends FormElement {
992
1024
  this.setValues([expression]);
993
1025
  }
994
1026
  this.input = '';
995
- if (!this.multi) {
1027
+ if (!this.isMultiMode) {
996
1028
  this.blur();
997
1029
  }
998
1030
  }
@@ -1003,6 +1035,18 @@ export class Select extends FormElement {
1003
1035
  this.completionOptions.length === 0 &&
1004
1036
  this.input.indexOf('@') > -1) {
1005
1037
  this.addInputAsValue();
1038
+ return;
1039
+ }
1040
+ // if we are in email mode and have a valid email, add it
1041
+ if (evt.key === 'Enter' &&
1042
+ this.emails &&
1043
+ this.input &&
1044
+ this.isValidEmail(this.input.trim()) &&
1045
+ this.visibleOptions.length === 0) {
1046
+ evt.preventDefault();
1047
+ const emailOption = { name: this.input.trim(), value: this.input.trim() };
1048
+ this.setSelectedOption(emailOption);
1049
+ return;
1006
1050
  }
1007
1051
  // see if we should open our options on a key event
1008
1052
  if (evt.key === 'Enter' ||
@@ -1017,7 +1061,7 @@ export class Select extends FormElement {
1017
1061
  }
1018
1062
  }
1019
1063
  // focus our last item on delete
1020
- if (this.multi && evt.key === 'Backspace' && !this.input) {
1064
+ if (this.isMultiMode && evt.key === 'Backspace' && !this.input) {
1021
1065
  if (this.visibleOptions.length > 0) {
1022
1066
  this.visibleOptions = [];
1023
1067
  return;
@@ -1130,7 +1174,8 @@ export class Select extends FormElement {
1130
1174
  }
1131
1175
  serializeValue(value) {
1132
1176
  // static options just use their value
1133
- if (!this.jsonValue && (this.staticOptions.length > 0 || this.tags)) {
1177
+ if (!this.jsonValue &&
1178
+ (this.staticOptions.length > 0 || this.isMultiMode)) {
1134
1179
  return value.value;
1135
1180
  }
1136
1181
  return super.serializeValue(value);
@@ -1159,13 +1204,17 @@ export class Select extends FormElement {
1159
1204
  }
1160
1205
  setValues(values) {
1161
1206
  const oldValues = this.values;
1162
- this.values = values;
1207
+ this.values.splice(0, this.values.length, ...values);
1163
1208
  this.requestUpdate('values', oldValues);
1209
+ this.updateInputs();
1210
+ this.fireEvent('change');
1164
1211
  }
1165
1212
  addValue(value) {
1166
1213
  const oldValues = [...this.values];
1167
1214
  this.values.push(value);
1168
1215
  this.requestUpdate('values', oldValues);
1216
+ this.updateInputs();
1217
+ this.fireEvent('change');
1169
1218
  }
1170
1219
  removeValue(valueToRemove) {
1171
1220
  const oldValues = [...this.values];
@@ -1186,17 +1235,23 @@ export class Select extends FormElement {
1186
1235
  }
1187
1236
  this.requestUpdate('values', oldValues);
1188
1237
  this.infoText = '';
1238
+ this.updateInputs();
1239
+ this.fireEvent('change');
1189
1240
  }
1190
1241
  popValue() {
1191
1242
  const oldValues = [...this.values];
1192
1243
  this.values.pop();
1193
1244
  this.requestUpdate('values', oldValues);
1194
1245
  this.infoText = '';
1246
+ this.updateInputs();
1247
+ this.fireEvent('change');
1195
1248
  }
1196
1249
  clear() {
1197
1250
  const oldValues = this.values;
1198
- this.values = [];
1251
+ this.values.splice(0, this.values.length);
1199
1252
  this.requestUpdate('values', oldValues);
1253
+ this.updateInputs();
1254
+ this.fireEvent('change');
1200
1255
  }
1201
1256
  shouldShowEmptyMessage() {
1202
1257
  return (this.attemptedOpen &&
@@ -1218,10 +1273,12 @@ export class Select extends FormElement {
1218
1273
  fromIdx < this.values.length &&
1219
1274
  toIdx < this.values.length) {
1220
1275
  const oldValues = [...this.values];
1221
- // Move the item from fromIdx to toIdx
1276
+ // Move the item using splice operations directly on this.values
1222
1277
  const movedItem = this.values.splice(fromIdx, 1)[0];
1223
1278
  this.values.splice(toIdx, 0, movedItem);
1224
1279
  this.requestUpdate('values', oldValues);
1280
+ this.updateInputs();
1281
+ this.fireEvent('change');
1225
1282
  }
1226
1283
  }
1227
1284
  }
@@ -1230,7 +1287,7 @@ export class Select extends FormElement {
1230
1287
  const placeholderDiv = html `
1231
1288
  <div class="placeholder">${placeholder}</div>
1232
1289
  `;
1233
- const clear = this.clearable && this.values.length > 0 && !this.multi
1290
+ const clear = this.clearable && this.values.length > 0 && !this.isMultiMode
1234
1291
  ? html `<temba-icon
1235
1292
  name="${Icon.select_clear}"
1236
1293
  size="1.1"
@@ -1239,8 +1296,8 @@ export class Select extends FormElement {
1239
1296
  />`
1240
1297
  : null;
1241
1298
  const classes = getClasses({
1242
- multi: this.multi,
1243
- single: !this.multi,
1299
+ multi: this.isMultiMode,
1300
+ single: !this.isMultiMode,
1244
1301
  searchable: this.searchable,
1245
1302
  empty: this.values.length === 0,
1246
1303
  options: this.visibleOptions.length > 0,
@@ -1273,8 +1330,8 @@ export class Select extends FormElement {
1273
1330
  </div>
1274
1331
  `
1275
1332
  : placeholderDiv;
1276
- const items = html `${!this.multi && !this.resolving ? input : null}
1277
- ${this.multi && this.values.length > 1
1333
+ const items = html `${!this.isMultiMode && !this.resolving ? input : null}
1334
+ ${this.isMultiMode && this.values.length > 1
1278
1335
  ? html `
1279
1336
  <temba-sortable-list
1280
1337
  horizontal
@@ -1314,7 +1371,7 @@ export class Select extends FormElement {
1314
1371
  : ''}
1315
1372
  "
1316
1373
  >
1317
- ${this.multi
1374
+ ${this.isMultiMode
1318
1375
  ? html `
1319
1376
  <div
1320
1377
  class="remove-item"
@@ -1361,7 +1418,7 @@ export class Select extends FormElement {
1361
1418
  : ''}
1362
1419
  "
1363
1420
  >
1364
- ${this.multi
1421
+ ${this.isMultiMode
1365
1422
  ? html `
1366
1423
  <div
1367
1424
  class="remove-item"
@@ -1387,12 +1444,12 @@ export class Select extends FormElement {
1387
1444
  </div>
1388
1445
  `
1389
1446
  : null}
1390
- ${!this.input || this.multi
1447
+ ${!this.input || this.isMultiMode
1391
1448
  ? this.renderSelectedItem(selected)
1392
1449
  : null}
1393
1450
  </div>
1394
1451
  `)}
1395
- ${this.multi ? input : null}`;
1452
+ ${this.isMultiMode ? input : null}`;
1396
1453
  return html `
1397
1454
 
1398
1455
  <temba-field
@@ -1428,7 +1485,7 @@ export class Select extends FormElement {
1428
1485
  ${clear}
1429
1486
 
1430
1487
  <slot name="right"></slot>
1431
- ${!this.tags
1488
+ ${!this.tags && !this.emails
1432
1489
  ? html `<div
1433
1490
  class="right-side arrow"
1434
1491
  style="display:block;margin-right:5px"
@@ -1587,6 +1644,9 @@ __decorate([
1587
1644
  __decorate([
1588
1645
  property({ type: Boolean })
1589
1646
  ], Select.prototype, "tags", void 0);
1647
+ __decorate([
1648
+ property({ type: Boolean })
1649
+ ], Select.prototype, "emails", void 0);
1590
1650
  __decorate([
1591
1651
  property({ type: Boolean, attribute: 'space_select' })
1592
1652
  ], Select.prototype, "spaceSelect", void 0);