@nyaruka/temba-components 0.131.1 → 0.131.3

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 (485) hide show
  1. package/.github/workflows/publish.yml +4 -1
  2. package/CHANGELOG.md +75 -1
  3. package/demo/components/floating-tabs/example.html +400 -0
  4. package/demo/components/flow/index.html +1 -1
  5. package/demo/data/flows/food-order.json +2 -2
  6. package/demo/data/flows/sample-flow.json +113 -125
  7. package/demo/data/flows/voicemail.json +613 -0
  8. package/demo/index.html +6 -0
  9. package/dist/locales/es.js +5 -5
  10. package/dist/locales/es.js.map +1 -1
  11. package/dist/locales/fr.js +5 -5
  12. package/dist/locales/fr.js.map +1 -1
  13. package/dist/locales/locale-codes.js +11 -2
  14. package/dist/locales/locale-codes.js.map +1 -1
  15. package/dist/locales/pt.js +5 -5
  16. package/dist/locales/pt.js.map +1 -1
  17. package/dist/static/svg/index.svg +1 -1
  18. package/dist/temba-components.js +1773 -662
  19. package/dist/temba-components.js.map +1 -1
  20. package/out-tsc/src/Icons.js +4 -1
  21. package/out-tsc/src/Icons.js.map +1 -1
  22. package/out-tsc/src/display/FloatingTab.js +167 -0
  23. package/out-tsc/src/display/FloatingTab.js.map +1 -0
  24. package/out-tsc/src/display/ProgressBar.js +22 -2
  25. package/out-tsc/src/display/ProgressBar.js.map +1 -1
  26. package/out-tsc/src/events.js.map +1 -1
  27. package/out-tsc/src/flow/CanvasMenu.js +200 -0
  28. package/out-tsc/src/flow/CanvasMenu.js.map +1 -0
  29. package/out-tsc/src/flow/CanvasNode.js +489 -47
  30. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  31. package/out-tsc/src/flow/Editor.js +1417 -67
  32. package/out-tsc/src/flow/Editor.js.map +1 -1
  33. package/out-tsc/src/flow/NodeEditor.js +479 -112
  34. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  35. package/out-tsc/src/flow/NodeTypeSelector.js +540 -0
  36. package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -0
  37. package/out-tsc/src/flow/StickyNote.js +12 -3
  38. package/out-tsc/src/flow/StickyNote.js.map +1 -1
  39. package/out-tsc/src/flow/actions/add_contact_groups.js +4 -3
  40. package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
  41. package/out-tsc/src/flow/actions/add_contact_urn.js +63 -4
  42. package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
  43. package/out-tsc/src/flow/actions/add_input_labels.js +4 -3
  44. package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
  45. package/out-tsc/src/flow/actions/play_audio.js +3 -2
  46. package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
  47. package/out-tsc/src/flow/actions/remove_contact_groups.js +7 -5
  48. package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
  49. package/out-tsc/src/flow/actions/request_optin.js +3 -2
  50. package/out-tsc/src/flow/actions/request_optin.js.map +1 -1
  51. package/out-tsc/src/flow/actions/say_msg.js +3 -2
  52. package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
  53. package/out-tsc/src/flow/actions/send_broadcast.js +77 -23
  54. package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
  55. package/out-tsc/src/flow/actions/send_email.js +5 -5
  56. package/out-tsc/src/flow/actions/send_email.js.map +1 -1
  57. package/out-tsc/src/flow/actions/send_msg.js +101 -21
  58. package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
  59. package/out-tsc/src/flow/actions/set_contact_channel.js +6 -9
  60. package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
  61. package/out-tsc/src/flow/actions/set_contact_field.js +20 -20
  62. package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
  63. package/out-tsc/src/flow/actions/set_contact_language.js +3 -2
  64. package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
  65. package/out-tsc/src/flow/actions/set_contact_name.js +3 -12
  66. package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
  67. package/out-tsc/src/flow/actions/set_contact_status.js +3 -2
  68. package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
  69. package/out-tsc/src/flow/actions/set_run_result.js +4 -3
  70. package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
  71. package/out-tsc/src/flow/actions/start_session.js +181 -6
  72. package/out-tsc/src/flow/actions/start_session.js.map +1 -1
  73. package/out-tsc/src/flow/config.js +11 -23
  74. package/out-tsc/src/flow/config.js.map +1 -1
  75. package/out-tsc/src/flow/currencies.js +45 -0
  76. package/out-tsc/src/flow/currencies.js.map +1 -0
  77. package/out-tsc/src/flow/nodes/shared-rules.js +257 -0
  78. package/out-tsc/src/flow/nodes/shared-rules.js.map +1 -0
  79. package/out-tsc/src/flow/nodes/shared.js +71 -0
  80. package/out-tsc/src/flow/nodes/shared.js.map +1 -0
  81. package/out-tsc/src/flow/nodes/split_by_airtime.js +211 -5
  82. package/out-tsc/src/flow/nodes/split_by_airtime.js.map +1 -1
  83. package/out-tsc/src/flow/nodes/split_by_contact_field.js +152 -3
  84. package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -1
  85. package/out-tsc/src/flow/nodes/split_by_expression.js +73 -2
  86. package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
  87. package/out-tsc/src/flow/nodes/split_by_groups.js +18 -10
  88. package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -1
  89. package/out-tsc/src/flow/nodes/split_by_intent.js +8 -0
  90. package/out-tsc/src/flow/nodes/split_by_intent.js.map +1 -0
  91. package/out-tsc/src/flow/nodes/split_by_llm.js +11 -3
  92. package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
  93. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +10 -3
  94. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
  95. package/out-tsc/src/flow/nodes/split_by_random.js +10 -4
  96. package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
  97. package/out-tsc/src/flow/nodes/split_by_resthook.js +113 -0
  98. package/out-tsc/src/flow/nodes/split_by_resthook.js.map +1 -0
  99. package/out-tsc/src/flow/nodes/split_by_run_result.js +211 -3
  100. package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -1
  101. package/out-tsc/src/flow/nodes/split_by_scheme.js +158 -2
  102. package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -1
  103. package/out-tsc/src/flow/nodes/split_by_subflow.js +13 -5
  104. package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
  105. package/out-tsc/src/flow/nodes/split_by_ticket.js +10 -3
  106. package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
  107. package/out-tsc/src/flow/nodes/split_by_webhook.js +10 -3
  108. package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
  109. package/out-tsc/src/flow/nodes/wait_for_digits.js +3 -2
  110. package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
  111. package/out-tsc/src/flow/nodes/wait_for_menu.js +3 -2
  112. package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
  113. package/out-tsc/src/flow/nodes/wait_for_response.js +38 -568
  114. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
  115. package/out-tsc/src/flow/types.js +86 -12
  116. package/out-tsc/src/flow/types.js.map +1 -1
  117. package/out-tsc/src/flow/utils.js +101 -14
  118. package/out-tsc/src/flow/utils.js.map +1 -1
  119. package/out-tsc/src/form/FieldRenderer.js +2 -4
  120. package/out-tsc/src/form/FieldRenderer.js.map +1 -1
  121. package/out-tsc/src/interfaces.js +3 -0
  122. package/out-tsc/src/interfaces.js.map +1 -1
  123. package/out-tsc/src/layout/FloatingWindow.js +346 -0
  124. package/out-tsc/src/layout/FloatingWindow.js.map +1 -0
  125. package/out-tsc/src/list/SortableList.js +98 -33
  126. package/out-tsc/src/list/SortableList.js.map +1 -1
  127. package/out-tsc/src/live/ContactChat.js +6 -25
  128. package/out-tsc/src/live/ContactChat.js.map +1 -1
  129. package/out-tsc/src/locales/es.js +5 -5
  130. package/out-tsc/src/locales/es.js.map +1 -1
  131. package/out-tsc/src/locales/fr.js +5 -5
  132. package/out-tsc/src/locales/fr.js.map +1 -1
  133. package/out-tsc/src/locales/locale-codes.js +11 -2
  134. package/out-tsc/src/locales/locale-codes.js.map +1 -1
  135. package/out-tsc/src/locales/pt.js +5 -5
  136. package/out-tsc/src/locales/pt.js.map +1 -1
  137. package/out-tsc/src/store/AppState.js +120 -0
  138. package/out-tsc/src/store/AppState.js.map +1 -1
  139. package/out-tsc/src/utils.js +254 -13
  140. package/out-tsc/src/utils.js.map +1 -1
  141. package/out-tsc/temba-modules.js +8 -0
  142. package/out-tsc/temba-modules.js.map +1 -1
  143. package/out-tsc/test/ActionHelper.js +3 -3
  144. package/out-tsc/test/ActionHelper.js.map +1 -1
  145. package/out-tsc/test/NodeHelper.js +6 -3
  146. package/out-tsc/test/NodeHelper.js.map +1 -1
  147. package/out-tsc/test/actions/add_contact_urn.test.js +202 -0
  148. package/out-tsc/test/actions/add_contact_urn.test.js.map +1 -0
  149. package/out-tsc/test/actions/send_broadcast.test.js +148 -0
  150. package/out-tsc/test/actions/send_broadcast.test.js.map +1 -0
  151. package/out-tsc/test/actions/send_email.test.js +17 -23
  152. package/out-tsc/test/actions/send_email.test.js.map +1 -1
  153. package/out-tsc/test/actions/send_msg.test.js +33 -15
  154. package/out-tsc/test/actions/send_msg.test.js.map +1 -1
  155. package/out-tsc/test/actions/start_session.test.js +116 -0
  156. package/out-tsc/test/actions/start_session.test.js.map +1 -0
  157. package/out-tsc/test/nodes/split_by_airtime.test.js +604 -0
  158. package/out-tsc/test/nodes/split_by_airtime.test.js.map +1 -0
  159. package/out-tsc/test/nodes/split_by_contact_field.test.js +387 -0
  160. package/out-tsc/test/nodes/split_by_contact_field.test.js.map +1 -0
  161. package/out-tsc/test/nodes/split_by_expression.test.js +614 -0
  162. package/out-tsc/test/nodes/split_by_expression.test.js.map +1 -0
  163. package/out-tsc/test/nodes/split_by_random.test.js +3 -3
  164. package/out-tsc/test/nodes/split_by_random.test.js.map +1 -1
  165. package/out-tsc/test/nodes/split_by_resthook.test.js +337 -0
  166. package/out-tsc/test/nodes/split_by_resthook.test.js.map +1 -0
  167. package/out-tsc/test/nodes/split_by_run_result.test.js +920 -0
  168. package/out-tsc/test/nodes/split_by_run_result.test.js.map +1 -0
  169. package/out-tsc/test/nodes/split_by_scheme.test.js +399 -0
  170. package/out-tsc/test/nodes/split_by_scheme.test.js.map +1 -0
  171. package/out-tsc/test/nodes/split_by_subflow.test.js +333 -0
  172. package/out-tsc/test/nodes/split_by_subflow.test.js.map +1 -0
  173. package/out-tsc/test/nodes/wait_for_digits.test.js +2 -2
  174. package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
  175. package/out-tsc/test/nodes/wait_for_response.test.js +2 -1
  176. package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -1
  177. package/out-tsc/test/temba-action-drag-between-nodes.test.js +252 -0
  178. package/out-tsc/test/temba-action-drag-between-nodes.test.js.map +1 -0
  179. package/out-tsc/test/temba-canvas-menu.test.js +122 -0
  180. package/out-tsc/test/temba-canvas-menu.test.js.map +1 -0
  181. package/out-tsc/test/temba-floating-tab.test.js +91 -0
  182. package/out-tsc/test/temba-floating-tab.test.js.map +1 -0
  183. package/out-tsc/test/temba-floating-window.test.js +301 -0
  184. package/out-tsc/test/temba-floating-window.test.js.map +1 -0
  185. package/out-tsc/test/temba-flow-editor-node.test.js +202 -2
  186. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
  187. package/out-tsc/test/temba-flow-editor.test.js +7 -8
  188. package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
  189. package/out-tsc/test/temba-localization.test.js +471 -0
  190. package/out-tsc/test/temba-localization.test.js.map +1 -0
  191. package/out-tsc/test/temba-node-editor.test.js +3 -1
  192. package/out-tsc/test/temba-node-editor.test.js.map +1 -1
  193. package/out-tsc/test/temba-node-type-selector.test.js +265 -0
  194. package/out-tsc/test/temba-node-type-selector.test.js.map +1 -0
  195. package/out-tsc/test/temba-omnibox.test.js +2 -1
  196. package/out-tsc/test/temba-omnibox.test.js.map +1 -1
  197. package/out-tsc/test/temba-sortable-list.test.js +51 -0
  198. package/out-tsc/test/temba-sortable-list.test.js.map +1 -1
  199. package/out-tsc/test/temba-utils-index.test.js +1 -27
  200. package/out-tsc/test/temba-utils-index.test.js.map +1 -1
  201. package/out-tsc/test/utils.test.js +20 -0
  202. package/out-tsc/test/utils.test.js.map +1 -1
  203. package/package.json +2 -1
  204. package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
  205. package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
  206. package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
  207. package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
  208. package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
  209. package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
  210. package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
  211. package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
  212. package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
  213. package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
  214. package/screenshots/truth/actions/add_contact_urn/editor/expression-facebook.png +0 -0
  215. package/screenshots/truth/actions/add_contact_urn/editor/expression-phone.png +0 -0
  216. package/screenshots/truth/actions/add_contact_urn/editor/facebook-id.png +0 -0
  217. package/screenshots/truth/actions/add_contact_urn/editor/instagram-handle.png +0 -0
  218. package/screenshots/truth/actions/add_contact_urn/editor/line-id.png +0 -0
  219. package/screenshots/truth/actions/add_contact_urn/editor/phone-number.png +0 -0
  220. package/screenshots/truth/actions/add_contact_urn/editor/telegram-id.png +0 -0
  221. package/screenshots/truth/actions/add_contact_urn/editor/viber-id.png +0 -0
  222. package/screenshots/truth/actions/add_contact_urn/editor/wechat-id.png +0 -0
  223. package/screenshots/truth/actions/add_contact_urn/editor/whatsapp.png +0 -0
  224. package/screenshots/truth/actions/add_contact_urn/render/expression-facebook.png +0 -0
  225. package/screenshots/truth/actions/add_contact_urn/render/expression-phone.png +0 -0
  226. package/screenshots/truth/actions/add_contact_urn/render/facebook-id.png +0 -0
  227. package/screenshots/truth/actions/add_contact_urn/render/instagram-handle.png +0 -0
  228. package/screenshots/truth/actions/add_contact_urn/render/line-id.png +0 -0
  229. package/screenshots/truth/actions/add_contact_urn/render/phone-number.png +0 -0
  230. package/screenshots/truth/actions/add_contact_urn/render/telegram-id.png +0 -0
  231. package/screenshots/truth/actions/add_contact_urn/render/viber-id.png +0 -0
  232. package/screenshots/truth/actions/add_contact_urn/render/wechat-id.png +0 -0
  233. package/screenshots/truth/actions/add_contact_urn/render/whatsapp.png +0 -0
  234. package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
  235. package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
  236. package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
  237. package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
  238. package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
  239. package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
  240. package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
  241. package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
  242. package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
  243. package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
  244. package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
  245. package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
  246. package/screenshots/truth/actions/send_broadcast/editor/contacts-only.png +0 -0
  247. package/screenshots/truth/actions/send_broadcast/editor/groups-and-contacts.png +0 -0
  248. package/screenshots/truth/actions/send_broadcast/editor/groups-only.png +0 -0
  249. package/screenshots/truth/actions/send_broadcast/editor/many-groups.png +0 -0
  250. package/screenshots/truth/actions/send_broadcast/editor/multiline-text.png +0 -0
  251. package/screenshots/truth/actions/send_broadcast/editor/with-attachments.png +0 -0
  252. package/screenshots/truth/actions/send_broadcast/render/contacts-only.png +0 -0
  253. package/screenshots/truth/actions/send_broadcast/render/groups-and-contacts.png +0 -0
  254. package/screenshots/truth/actions/send_broadcast/render/groups-only.png +0 -0
  255. package/screenshots/truth/actions/send_broadcast/render/many-groups.png +0 -0
  256. package/screenshots/truth/actions/send_broadcast/render/multiline-text.png +0 -0
  257. package/screenshots/truth/actions/send_broadcast/render/with-attachments.png +0 -0
  258. package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
  259. package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
  260. package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
  261. package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
  262. package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
  263. package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
  264. package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
  265. package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
  266. package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
  267. package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
  268. package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
  269. package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
  270. package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
  271. package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
  272. package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
  273. package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
  274. package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
  275. package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
  276. package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
  277. package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
  278. package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
  279. package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
  280. package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
  281. package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
  282. package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
  283. package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
  284. package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
  285. package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
  286. package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
  287. package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
  288. package/screenshots/truth/actions/start_session/editor/contact-query.png +0 -0
  289. package/screenshots/truth/actions/start_session/editor/contacts-only.png +0 -0
  290. package/screenshots/truth/actions/start_session/editor/create-contact.png +0 -0
  291. package/screenshots/truth/actions/start_session/editor/groups-and-contacts.png +0 -0
  292. package/screenshots/truth/actions/start_session/editor/groups-only.png +0 -0
  293. package/screenshots/truth/actions/start_session/editor/many-recipients.png +0 -0
  294. package/screenshots/truth/actions/start_session/render/contact-query.png +0 -0
  295. package/screenshots/truth/actions/start_session/render/contacts-only.png +0 -0
  296. package/screenshots/truth/actions/start_session/render/create-contact.png +0 -0
  297. package/screenshots/truth/actions/start_session/render/groups-and-contacts.png +0 -0
  298. package/screenshots/truth/actions/start_session/render/groups-only.png +0 -0
  299. package/screenshots/truth/actions/start_session/render/many-recipients.png +0 -0
  300. package/screenshots/truth/canvas-menu/open.png +0 -0
  301. package/screenshots/truth/editor/router.png +0 -0
  302. package/screenshots/truth/editor/wait.png +0 -0
  303. package/screenshots/truth/floating-tab/default.png +0 -0
  304. package/screenshots/truth/floating-tab/gray.png +0 -0
  305. package/screenshots/truth/floating-tab/green.png +0 -0
  306. package/screenshots/truth/floating-tab/hidden.png +0 -0
  307. package/screenshots/truth/floating-tab/hover.png +0 -0
  308. package/screenshots/truth/floating-tab/purple.png +0 -0
  309. package/screenshots/truth/floating-window/chromeless.png +0 -0
  310. package/screenshots/truth/floating-window/custom-size.png +0 -0
  311. package/screenshots/truth/floating-window/default.png +0 -0
  312. package/screenshots/truth/floating-window/with-header.png +0 -0
  313. package/screenshots/truth/list/fields-dragging.png +0 -0
  314. package/screenshots/truth/list/sortable-dragging.png +0 -0
  315. package/screenshots/truth/node-type-selector/action-mode.png +0 -0
  316. package/screenshots/truth/node-type-selector/split-mode.png +0 -0
  317. package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
  318. package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
  319. package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
  320. package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
  321. package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
  322. package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
  323. package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
  324. package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
  325. package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
  326. package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
  327. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  328. package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
  329. package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
  330. package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
  331. package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
  332. package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
  333. package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
  334. package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
  335. package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
  336. package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
  337. package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
  338. package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
  339. package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
  340. package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
  341. package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
  342. package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
  343. package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
  344. package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
  345. package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
  346. package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
  347. package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
  348. package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
  349. package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
  350. package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
  351. package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
  352. package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
  353. package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
  354. package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
  355. package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
  356. package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
  357. package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
  358. package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
  359. package/src/Icons.ts +4 -1
  360. package/src/display/FloatingTab.ts +174 -0
  361. package/src/display/ProgressBar.ts +22 -2
  362. package/src/events.ts +2 -8
  363. package/src/flow/CanvasMenu.ts +217 -0
  364. package/src/flow/CanvasNode.ts +596 -40
  365. package/src/flow/Editor.ts +1721 -45
  366. package/src/flow/NodeEditor.ts +621 -144
  367. package/src/flow/NodeTypeSelector.ts +636 -0
  368. package/src/flow/StickyNote.ts +12 -3
  369. package/src/flow/actions/add_contact_groups.ts +5 -4
  370. package/src/flow/actions/add_contact_urn.ts +78 -4
  371. package/src/flow/actions/add_input_labels.ts +5 -4
  372. package/src/flow/actions/play_audio.ts +3 -2
  373. package/src/flow/actions/remove_contact_groups.ts +16 -6
  374. package/src/flow/actions/request_optin.ts +3 -2
  375. package/src/flow/actions/say_msg.ts +3 -2
  376. package/src/flow/actions/send_broadcast.ts +86 -23
  377. package/src/flow/actions/send_email.ts +12 -6
  378. package/src/flow/actions/send_msg.ts +155 -34
  379. package/src/flow/actions/set_contact_channel.ts +6 -11
  380. package/src/flow/actions/set_contact_field.ts +21 -25
  381. package/src/flow/actions/set_contact_language.ts +11 -4
  382. package/src/flow/actions/set_contact_name.ts +4 -15
  383. package/src/flow/actions/set_contact_status.ts +4 -3
  384. package/src/flow/actions/set_run_result.ts +5 -4
  385. package/src/flow/actions/start_session.ts +210 -6
  386. package/src/flow/config.ts +11 -23
  387. package/src/flow/currencies.ts +51 -0
  388. package/src/flow/nodes/shared-rules.ts +301 -0
  389. package/src/flow/nodes/shared.ts +87 -0
  390. package/src/flow/nodes/split_by_airtime.ts +255 -5
  391. package/src/flow/nodes/split_by_contact_field.ts +195 -3
  392. package/src/flow/nodes/split_by_expression.ts +104 -2
  393. package/src/flow/nodes/split_by_groups.ts +26 -11
  394. package/src/flow/nodes/split_by_intent.ts +8 -0
  395. package/src/flow/nodes/split_by_llm.ts +22 -4
  396. package/src/flow/nodes/split_by_llm_categorize.ts +22 -5
  397. package/src/flow/nodes/split_by_random.ts +16 -6
  398. package/src/flow/nodes/split_by_resthook.ts +140 -0
  399. package/src/flow/nodes/split_by_run_result.ts +259 -3
  400. package/src/flow/nodes/split_by_scheme.ts +202 -2
  401. package/src/flow/nodes/split_by_subflow.ts +17 -5
  402. package/src/flow/nodes/split_by_ticket.ts +15 -4
  403. package/src/flow/nodes/split_by_webhook.ts +17 -6
  404. package/src/flow/nodes/wait_for_digits.ts +3 -2
  405. package/src/flow/nodes/wait_for_menu.ts +3 -2
  406. package/src/flow/nodes/wait_for_response.ts +59 -680
  407. package/src/flow/types.ts +156 -23
  408. package/src/flow/utils.ts +108 -14
  409. package/src/form/FieldRenderer.ts +2 -4
  410. package/src/interfaces.ts +3 -0
  411. package/src/layout/FloatingWindow.ts +386 -0
  412. package/src/list/SortableList.ts +109 -34
  413. package/src/live/ContactChat.ts +7 -25
  414. package/src/locales/es.ts +18 -13
  415. package/src/locales/fr.ts +18 -13
  416. package/src/locales/locale-codes.ts +11 -2
  417. package/src/locales/pt.ts +18 -13
  418. package/src/store/AppState.ts +173 -0
  419. package/src/store/flow-definition.d.ts +2 -5
  420. package/src/utils.ts +332 -12
  421. package/static/api/channels.json +46 -0
  422. package/static/api/llms.json +18 -0
  423. package/static/api/resthooks.json +31 -0
  424. package/static/svg/index.svg +1 -1
  425. package/static/svg/work/traced/lightning-02.svg +1 -0
  426. package/static/svg/work/used/lightning-02.svg +3 -0
  427. package/temba-modules.ts +8 -0
  428. package/test/ActionHelper.ts +3 -3
  429. package/test/NodeHelper.ts +6 -3
  430. package/test/actions/add_contact_urn.test.ts +287 -0
  431. package/test/actions/send_broadcast.test.ts +190 -0
  432. package/test/actions/send_email.test.ts +17 -23
  433. package/test/actions/send_msg.test.ts +39 -15
  434. package/test/actions/start_session.test.ts +151 -0
  435. package/test/nodes/split_by_airtime.test.ts +673 -0
  436. package/test/nodes/split_by_contact_field.test.ts +451 -0
  437. package/test/nodes/split_by_expression.test.ts +751 -0
  438. package/test/nodes/split_by_random.test.ts +3 -3
  439. package/test/nodes/split_by_resthook.test.ts +398 -0
  440. package/test/nodes/split_by_run_result.test.ts +1109 -0
  441. package/test/nodes/split_by_scheme.test.ts +486 -0
  442. package/test/nodes/split_by_subflow.test.ts +381 -0
  443. package/test/nodes/wait_for_digits.test.ts +2 -2
  444. package/test/nodes/wait_for_response.test.ts +2 -1
  445. package/test/temba-action-drag-between-nodes.test.ts +301 -0
  446. package/test/temba-canvas-menu.test.ts +156 -0
  447. package/test/temba-floating-tab.test.ts +110 -0
  448. package/test/temba-floating-window.test.ts +477 -0
  449. package/test/temba-flow-editor-node.test.ts +246 -2
  450. package/test/temba-flow-editor.test.ts +7 -8
  451. package/test/temba-localization.test.ts +611 -0
  452. package/test/temba-node-editor.test.ts +3 -1
  453. package/test/temba-node-type-selector.test.ts +355 -0
  454. package/test/temba-omnibox.test.ts +2 -1
  455. package/test/temba-sortable-list.test.ts +69 -0
  456. package/test/temba-utils-index.test.ts +0 -35
  457. package/test/utils.test.ts +22 -0
  458. package/test-assets/contacts/history.json +14 -21
  459. package/test-assets/select/llms.json +2 -2
  460. package/web-dev-server.config.mjs +49 -1
  461. package/web-test-runner.config.mjs +0 -1
  462. package/out-tsc/src/flow/actions/call_classifier.js +0 -11
  463. package/out-tsc/src/flow/actions/call_classifier.js.map +0 -1
  464. package/out-tsc/src/flow/actions/call_resthook.js +0 -11
  465. package/out-tsc/src/flow/actions/call_resthook.js.map +0 -1
  466. package/out-tsc/src/flow/actions/split_by_expression_example.js +0 -77
  467. package/out-tsc/src/flow/actions/split_by_expression_example.js.map +0 -1
  468. package/out-tsc/src/flow/actions/transfer_airtime.js +0 -11
  469. package/out-tsc/src/flow/actions/transfer_airtime.js.map +0 -1
  470. package/out-tsc/src/flow/nodes/wait_for_audio.js +0 -7
  471. package/out-tsc/src/flow/nodes/wait_for_audio.js.map +0 -1
  472. package/out-tsc/src/flow/nodes/wait_for_image.js +0 -7
  473. package/out-tsc/src/flow/nodes/wait_for_image.js.map +0 -1
  474. package/out-tsc/src/flow/nodes/wait_for_location.js +0 -7
  475. package/out-tsc/src/flow/nodes/wait_for_location.js.map +0 -1
  476. package/out-tsc/src/flow/nodes/wait_for_video.js +0 -7
  477. package/out-tsc/src/flow/nodes/wait_for_video.js.map +0 -1
  478. package/src/flow/actions/call_classifier.ts +0 -12
  479. package/src/flow/actions/call_resthook.ts +0 -12
  480. package/src/flow/actions/split_by_expression_example.ts +0 -88
  481. package/src/flow/actions/transfer_airtime.ts +0 -12
  482. package/src/flow/nodes/wait_for_audio.ts +0 -7
  483. package/src/flow/nodes/wait_for_image.ts +0 -7
  484. package/src/flow/nodes/wait_for_location.ts +0 -7
  485. package/src/flow/nodes/wait_for_video.ts +0 -7
@@ -0,0 +1,386 @@
1
+ import { css, html, PropertyValueMap, TemplateResult } from 'lit';
2
+ import { property } from 'lit/decorators.js';
3
+ import { RapidElement } from '../RapidElement';
4
+ import { CustomEventType } from '../interfaces';
5
+ import { getClasses } from '../utils';
6
+ import { FloatingTab } from '../display/FloatingTab';
7
+
8
+ export class FloatingWindow extends RapidElement {
9
+ static get styles() {
10
+ return css`
11
+ .window.hidden {
12
+ transform: translateX(100%);
13
+ opacity: 0;
14
+ pointer-events: none;
15
+ }
16
+
17
+ .window {
18
+ transition: transform var(--transition-duration, 300ms) ease-in-out,
19
+ opacity var(--transition-duration, 300ms) ease-in-out;
20
+ position: fixed;
21
+ z-index: 9999;
22
+ top: 100px;
23
+ background: white;
24
+ border-radius: 8px;
25
+ box-shadow: -4px 4px 20px rgba(0, 0, 0, 0.3);
26
+ display: flex;
27
+ flex-direction: column;
28
+ overflow: hidden;
29
+ }
30
+
31
+ .window.chromeless {
32
+ background: transparent;
33
+ border-radius: 0;
34
+ box-shadow: none;
35
+ }
36
+
37
+ .window.dragging {
38
+ user-select: none;
39
+ cursor: move;
40
+ }
41
+
42
+ .header {
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: space-between;
46
+ padding: 6px 6px;
47
+ background: var(--header-color, var(--color-primary-light, #f3f4f6));
48
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
49
+ cursor: move;
50
+ user-select: none;
51
+ }
52
+
53
+ .title {
54
+ font-weight: 600;
55
+ font-size: 16px;
56
+ color: white;
57
+ padding-left: 8px;
58
+ }
59
+
60
+ .close-button {
61
+ background: none;
62
+ border: none;
63
+ cursor: pointer;
64
+ padding: 4px;
65
+ display: flex;
66
+ align-items: center;
67
+ justify-content: center;
68
+ border-radius: 4px;
69
+ transition: background-color calc(var(--transition-duration, 150ms) / 2)
70
+ ease-in-out;
71
+ }
72
+
73
+ .close-button:hover {
74
+ background-color: rgba(255, 255, 255, 0.2);
75
+ }
76
+
77
+ .close-button temba-icon {
78
+ --icon-color: white;
79
+ }
80
+
81
+ .body {
82
+ flex: 1;
83
+ overflow-y: auto;
84
+ padding: 16px;
85
+ }
86
+
87
+ .window.chromeless .body {
88
+ padding: 0;
89
+ }
90
+
91
+ ::slotted(.drag-handle) {
92
+ cursor: move;
93
+ user-select: none;
94
+ border: 1px solid red;
95
+ }
96
+ `;
97
+ }
98
+
99
+ @property({ type: String })
100
+ header = '';
101
+
102
+ @property({ type: Number })
103
+ width = 500;
104
+
105
+ @property({ type: Number })
106
+ minHeight = 200;
107
+
108
+ @property({ type: Number })
109
+ maxHeight = 800;
110
+
111
+ @property({ type: Number })
112
+ top = 100;
113
+
114
+ @property({ type: Number })
115
+ left = -1; // -1 means calculate from right side
116
+
117
+ @property({ type: Boolean })
118
+ hidden = true;
119
+
120
+ @property({ type: Boolean })
121
+ dragging = false;
122
+
123
+ @property({ type: Boolean })
124
+ chromeless = false;
125
+
126
+ @property({ type: String })
127
+ color = '#6B7280';
128
+
129
+ private dragStartX = 0;
130
+ private dragStartY = 0;
131
+ private dragOffsetX = 0;
132
+ private dragOffsetY = 0;
133
+ private positionFromRight = false;
134
+ private defaultTop = 100;
135
+ private defaultLeft = -1;
136
+
137
+ public firstUpdated(
138
+ changes: PropertyValueMap<any> | Map<PropertyKey, unknown>
139
+ ): void {
140
+ super.firstUpdated(changes);
141
+
142
+ // store the default position from properties
143
+ this.defaultTop = this.top;
144
+ this.defaultLeft = this.left;
145
+
146
+ // determine if we should position from right side
147
+ if (this.left === -1) {
148
+ this.positionFromRight = true;
149
+ }
150
+
151
+ // set up drag handle listeners for chromeless windows
152
+ if (this.chromeless) {
153
+ this.setupDragHandles();
154
+ }
155
+
156
+ // listen for window resize to keep window in bounds
157
+ window.addEventListener('resize', this.handleResize);
158
+ }
159
+
160
+ disconnectedCallback(): void {
161
+ super.disconnectedCallback();
162
+ window.removeEventListener('resize', this.handleResize);
163
+ }
164
+
165
+ private setupDragHandles() {
166
+ // listen for mousedown on slotted content
167
+ this.addEventListener('mousedown', this.handleSlotMouseDown);
168
+ }
169
+
170
+ private handleSlotMouseDown = (event: MouseEvent) => {
171
+ // check if the target or any parent has the drag-handle class
172
+ const target = event.target as HTMLElement;
173
+ const dragHandle = target.closest('.drag-handle');
174
+
175
+ if (!dragHandle) {
176
+ return;
177
+ }
178
+
179
+ this.dragging = true;
180
+ this.dragStartX = event.clientX;
181
+ this.dragStartY = event.clientY;
182
+ this.dragOffsetX = this.left;
183
+ this.dragOffsetY = this.top;
184
+
185
+ document.addEventListener('mousemove', this.handleMouseMove);
186
+ document.addEventListener('mouseup', this.handleMouseUp);
187
+
188
+ event.preventDefault();
189
+ };
190
+
191
+ public updated(
192
+ changes: PropertyValueMap<any> | Map<PropertyKey, unknown>
193
+ ): void {
194
+ super.updated(changes);
195
+ if (changes.has('hidden')) {
196
+ this.classList.toggle('hidden', this.hidden);
197
+
198
+ // when hiding, reset positioning behavior to original
199
+ if (this.hidden && !changes.get('hidden')) {
200
+ if (this.defaultLeft === -1) {
201
+ this.positionFromRight = true;
202
+ }
203
+ }
204
+
205
+ // reset to default position when showing
206
+ if (!this.hidden && changes.get('hidden')) {
207
+ // reset top to default
208
+ this.top = this.defaultTop;
209
+
210
+ // if positioned from right, recalculate based on current viewport
211
+ if (this.positionFromRight) {
212
+ this.left = window.innerWidth - this.width - 20;
213
+ } else {
214
+ // reset left to default
215
+ this.left = this.defaultLeft;
216
+ }
217
+ }
218
+ }
219
+
220
+ // setup drag handles if chromeless changed to true
221
+ if (changes.has('chromeless') && this.chromeless) {
222
+ this.setupDragHandles();
223
+ }
224
+ }
225
+
226
+ private handleClose() {
227
+ this.hidden = true;
228
+ // show all tabs when window is closed
229
+ FloatingTab.showAllTabs();
230
+ this.fireCustomEvent(CustomEventType.DialogHidden);
231
+ }
232
+
233
+ private handleHeaderMouseDown(event: MouseEvent) {
234
+ // don't start drag if clicking on close button
235
+ if ((event.target as HTMLElement).closest('.close-button')) {
236
+ return;
237
+ }
238
+
239
+ this.dragging = true;
240
+ this.dragStartX = event.clientX;
241
+ this.dragStartY = event.clientY;
242
+ this.dragOffsetX = this.left;
243
+ this.dragOffsetY = this.top;
244
+
245
+ document.addEventListener('mousemove', this.handleMouseMove);
246
+ document.addEventListener('mouseup', this.handleMouseUp);
247
+
248
+ event.preventDefault();
249
+ }
250
+
251
+ private handleMouseMove = (event: MouseEvent) => {
252
+ if (!this.dragging) return;
253
+
254
+ const deltaX = event.clientX - this.dragStartX;
255
+ const deltaY = event.clientY - this.dragStartY;
256
+
257
+ this.left = this.dragOffsetX + deltaX;
258
+ this.top = this.dragOffsetY + deltaY;
259
+
260
+ // keep window within viewport bounds with 20px padding
261
+ const padding = 20;
262
+ this.left = Math.max(
263
+ padding,
264
+ Math.min(this.left, window.innerWidth - this.width - padding)
265
+ );
266
+
267
+ // get the actual rendered height of the window element
268
+ const windowElement = this.shadowRoot?.querySelector(
269
+ '.window'
270
+ ) as HTMLElement;
271
+ const currentHeight =
272
+ windowElement?.offsetHeight || this.maxHeight || window.innerHeight;
273
+ const maxTop = Math.max(
274
+ padding,
275
+ window.innerHeight - currentHeight - padding
276
+ );
277
+ this.top = Math.max(padding, Math.min(this.top, maxTop));
278
+ };
279
+
280
+ private handleMouseUp = () => {
281
+ this.dragging = false;
282
+ document.removeEventListener('mousemove', this.handleMouseMove);
283
+ document.removeEventListener('mouseup', this.handleMouseUp);
284
+
285
+ // once user drags the window, stop auto-positioning from right
286
+ this.positionFromRight = false;
287
+ };
288
+
289
+ private handleResize = () => {
290
+ // only constrain position if window is visible
291
+ if (this.hidden) return;
292
+
293
+ const padding = 20;
294
+ const windowElement = this.shadowRoot?.querySelector(
295
+ '.window'
296
+ ) as HTMLElement;
297
+ const currentHeight =
298
+ windowElement?.offsetHeight || this.maxHeight || window.innerHeight;
299
+
300
+ // if positioned from right, always recalculate from right edge
301
+ if (this.positionFromRight) {
302
+ this.left = window.innerWidth - this.width - padding;
303
+ } else {
304
+ // only adjust left if out of bounds
305
+ const minLeft = padding;
306
+ const maxLeft = window.innerWidth - this.width - padding;
307
+
308
+ if (this.left < minLeft) {
309
+ this.left = minLeft;
310
+ } else if (this.left > maxLeft) {
311
+ this.left = maxLeft;
312
+ }
313
+ }
314
+
315
+ // only adjust top if out of bounds
316
+ const minTop = padding;
317
+ const maxTop = Math.max(
318
+ padding,
319
+ window.innerHeight - currentHeight - padding
320
+ );
321
+
322
+ if (this.top < minTop) {
323
+ this.top = minTop;
324
+ } else if (this.top > maxTop) {
325
+ this.top = maxTop;
326
+ }
327
+ };
328
+
329
+ public show() {
330
+ this.hidden = false;
331
+ }
332
+
333
+ public hide() {
334
+ this.hidden = true;
335
+ }
336
+
337
+ public close() {
338
+ this.hidden = true;
339
+ // show all tabs when window is closed
340
+ FloatingTab.showAllTabs();
341
+ this.fireCustomEvent(CustomEventType.DialogHidden);
342
+ }
343
+
344
+ public render(): TemplateResult {
345
+ const minHeightStyle = this.minHeight
346
+ ? `min-height: ${this.minHeight}px;`
347
+ : '';
348
+ const maxHeightStyle = this.maxHeight
349
+ ? `max-height: ${this.maxHeight}px;`
350
+ : '';
351
+
352
+ const windowStyle = `
353
+ width: ${this.width}px;
354
+ ${minHeightStyle}
355
+ ${maxHeightStyle}
356
+ top: ${this.top}px;
357
+ left: ${this.left}px;
358
+ --header-color: ${this.color};
359
+ `;
360
+
361
+ const windowClasses = getClasses({
362
+ window: true,
363
+ dragging: this.dragging,
364
+ hidden: this.hidden,
365
+ chromeless: this.chromeless
366
+ });
367
+
368
+ return html`
369
+ <div class="${windowClasses}" style="${windowStyle}">
370
+ ${!this.chromeless
371
+ ? html`
372
+ <div class="header" @mousedown=${this.handleHeaderMouseDown}>
373
+ <div class="title">${this.header}</div>
374
+ <button class="close-button" @click=${this.handleClose}>
375
+ <temba-icon name="close" size="1.5"></temba-icon>
376
+ </button>
377
+ </div>
378
+ `
379
+ : ''}
380
+ <div class="body">
381
+ <slot></slot>
382
+ </div>
383
+ </div>
384
+ `;
385
+ }
386
+ }
@@ -9,6 +9,10 @@ import { RapidElement } from '../RapidElement';
9
9
 
10
10
  // how far we have to drag before it starts
11
11
  const DRAG_THRESHOLD = 2;
12
+
13
+ // padding around container for external drag detection
14
+ const EXTERNAL_DRAG_PADDING = 50;
15
+
12
16
  export class SortableList extends RapidElement {
13
17
  originalDownDisplay: string;
14
18
  static get styles() {
@@ -65,6 +69,9 @@ export class SortableList extends RapidElement {
65
69
  @property({ type: String })
66
70
  gap: string = '0em';
67
71
 
72
+ @property({ type: Boolean })
73
+ externalDrag: boolean = false;
74
+
68
75
  /**
69
76
  * Optional callback to allow parent components to customize the ghost node.
70
77
  * Called after the ghost node is cloned but before it is appended to the DOM.
@@ -86,6 +93,7 @@ export class SortableList extends RapidElement {
86
93
  dropPlaceholder: HTMLDivElement = null;
87
94
  pendingDropIndex = -1;
88
95
  pendingTargetElement: HTMLElement = null;
96
+ isExternalDrag = false;
89
97
 
90
98
  private clickBlocker: ((e: MouseEvent) => void) | null = null;
91
99
 
@@ -108,6 +116,20 @@ export class SortableList extends RapidElement {
108
116
  return eles;
109
117
  }
110
118
 
119
+ private isMouseOverContainer(mouseX: number, mouseY: number): boolean {
120
+ const container = this.shadowRoot.querySelector('.container');
121
+ if (!container) return false;
122
+
123
+ const rect = container.getBoundingClientRect();
124
+ // add some padding to make it easier to stay within the container
125
+ return (
126
+ mouseX >= rect.left - EXTERNAL_DRAG_PADDING &&
127
+ mouseX <= rect.right + EXTERNAL_DRAG_PADDING &&
128
+ mouseY >= rect.top - EXTERNAL_DRAG_PADDING &&
129
+ mouseY <= rect.bottom + EXTERNAL_DRAG_PADDING
130
+ );
131
+ }
132
+
111
133
  private cloneElementWithState(element: HTMLElement): HTMLElement {
112
134
  // First create a basic clone
113
135
  const clone = element.cloneNode(true) as HTMLElement;
@@ -305,6 +327,8 @@ export class SortableList extends RapidElement {
305
327
  this.dropPlaceholder.style.minHeight = rect.height + 'px';
306
328
  this.dropPlaceholder.style.borderRadius = 'var(--curvature)';
307
329
  this.dropPlaceholder.style.flexShrink = '0';
330
+ this.dropPlaceholder.style.background = '#f3f4f6';
331
+ this.dropPlaceholder.style.border = '2px dashed #d1d5db';
308
332
  }
309
333
 
310
334
  // Insert the placeholder in the correct position in the DOM
@@ -335,10 +359,8 @@ export class SortableList extends RapidElement {
335
359
  this.dropPlaceholder.style.minHeight = rect.height + 'px';
336
360
  this.dropPlaceholder.style.borderRadius = 'var(--curvature)';
337
361
  this.dropPlaceholder.style.flexShrink = '0';
338
- this.dropPlaceholder.style.background =
339
- 'rgba(var(--color-primary-rgb), 0.1)';
340
- this.dropPlaceholder.style.border =
341
- '2px dashed rgba(var(--color-primary-rgb), 0.3)';
362
+ this.dropPlaceholder.style.background = '#f3f4f6';
363
+ this.dropPlaceholder.style.border = '2px dashed #d1d5db';
342
364
 
343
365
  // Insert the placeholder right after the hidden original element
344
366
  this.downEle.insertAdjacentElement('afterend', this.dropPlaceholder);
@@ -440,41 +462,86 @@ export class SortableList extends RapidElement {
440
462
  this.ghostElement.style.left = event.clientX - this.xOffset + 'px';
441
463
  this.ghostElement.style.top = event.clientY - this.yOffset + 'px';
442
464
 
443
- const targetInfo = this.getDropTargetInfo(event.clientX, event.clientY);
444
- if (targetInfo) {
445
- const { element: targetElement, insertAfter } = targetInfo;
446
- const targetIdx = this.getRowIndex(targetElement.id);
465
+ // check if the drag is over the container (only if external dragging is allowed)
466
+ const isOverContainer = this.externalDrag
467
+ ? this.isMouseOverContainer(event.clientX, event.clientY)
468
+ : true; // always consider "over container" if external drag is disabled
447
469
 
448
- // Use the original drag index we captured before moving the element
449
- const originalDragIdx = this.originalDragIndex;
470
+ // detect transition between internal and external drag (only if allowed)
471
+ if (this.externalDrag && !isOverContainer && !this.isExternalDrag) {
472
+ // transitioning to external drag
473
+ this.isExternalDrag = true;
474
+ this.hideDropPlaceholder();
450
475
 
451
- // Calculate where the dragged element will end up in the final array
452
- // targetIdx is the position of target element in current DOM (missing dragged element)
476
+ // hide the ghost element when dragging externally
477
+ if (this.ghostElement) {
478
+ this.ghostElement.style.display = 'none';
479
+ }
453
480
 
454
- let dropIdx;
455
- if (targetIdx < originalDragIdx) {
456
- // Target is before the original drag position - moving backward
457
- dropIdx = insertAfter ? targetIdx + 1 : targetIdx;
458
- } else {
459
- // Target was originally after the drag position - moving forward
460
- // When moving the dragged element forward (i.e., to a higher index), the targetIdx is based on the current DOM,
461
- // which no longer includes the dragged element. This means all elements after the original position have shifted left by one,
462
- // so we need to subtract 1 from targetIdx to get the correct insertion index. If inserting after the target, we use targetIdx as is.
463
- dropIdx = insertAfter ? targetIdx : targetIdx - 1;
481
+ this.fireCustomEvent(CustomEventType.DragExternal, {
482
+ id: this.downEle.id,
483
+ mouseX: event.clientX,
484
+ mouseY: event.clientY
485
+ });
486
+ } else if (this.externalDrag && isOverContainer && this.isExternalDrag) {
487
+ // transitioning back to internal drag
488
+ this.isExternalDrag = false;
489
+
490
+ // show the ghost element again when dragging internally
491
+ if (this.ghostElement) {
492
+ this.ghostElement.style.display = 'block';
464
493
  }
465
494
 
466
- // Store pending drop info but don't fire event yet
467
- this.dropTargetId = targetElement.id;
468
- this.pendingDropIndex = dropIdx;
469
- this.pendingTargetElement = targetElement;
495
+ this.fireCustomEvent(CustomEventType.DragInternal, {
496
+ id: this.downEle.id
497
+ });
498
+ }
499
+
500
+ // only show drop placeholder and calculate drop position if internal drag
501
+ if (!this.isExternalDrag) {
502
+ const targetInfo = this.getDropTargetInfo(event.clientX, event.clientY);
503
+ if (targetInfo) {
504
+ const { element: targetElement, insertAfter } = targetInfo;
505
+ const targetIdx = this.getRowIndex(targetElement.id);
506
+
507
+ // Use the original drag index we captured before moving the element
508
+ const originalDragIdx = this.originalDragIndex;
509
+
510
+ // Calculate where the dragged element will end up in the final array
511
+ // targetIdx is the position of target element in current DOM (missing dragged element)
512
+
513
+ let dropIdx;
514
+ if (targetIdx < originalDragIdx) {
515
+ // Target is before the original drag position - moving backward
516
+ dropIdx = insertAfter ? targetIdx + 1 : targetIdx;
517
+ } else {
518
+ // Target was originally after the drag position - moving forward
519
+ // When moving the dragged element forward (i.e., to a higher index), the targetIdx is based on the current DOM,
520
+ // which no longer includes the dragged element. This means all elements after the original position have shifted left by one,
521
+ // so we need to subtract 1 from targetIdx to get the correct insertion index. If inserting after the target, we use targetIdx as is.
522
+ dropIdx = insertAfter ? targetIdx : targetIdx - 1;
523
+ }
470
524
 
471
- // Show drop placeholder
472
- this.showDropPlaceholder(targetElement, insertAfter);
525
+ // Store pending drop info but don't fire event yet
526
+ this.dropTargetId = targetElement.id;
527
+ this.pendingDropIndex = dropIdx;
528
+ this.pendingTargetElement = targetElement;
529
+
530
+ // Show drop placeholder
531
+ this.showDropPlaceholder(targetElement, insertAfter);
532
+ } else {
533
+ this.hideDropPlaceholder();
534
+ this.dropTargetId = null;
535
+ this.pendingDropIndex = -1;
536
+ this.pendingTargetElement = null;
537
+ }
473
538
  } else {
474
- this.hideDropPlaceholder();
475
- this.dropTargetId = null;
476
- this.pendingDropIndex = -1;
477
- this.pendingTargetElement = null;
539
+ // external drag - continue firing external drag events with updated position
540
+ this.fireCustomEvent(CustomEventType.DragExternal, {
541
+ id: this.downEle.id,
542
+ mouseX: event.clientX,
543
+ mouseY: event.clientY
544
+ });
478
545
  }
479
546
  }
480
547
  }
@@ -498,7 +565,11 @@ export class SortableList extends RapidElement {
498
565
  this.hideDropPlaceholder();
499
566
 
500
567
  // fire the order changed event only when dropped if we have a valid drop position
501
- if (this.pendingDropIndex >= 0 && this.pendingTargetElement) {
568
+ if (
569
+ !this.isExternalDrag &&
570
+ this.pendingDropIndex >= 0 &&
571
+ this.pendingTargetElement
572
+ ) {
502
573
  // Use the original drag index we captured before hiding the element
503
574
  const originalDragIdx = this.originalDragIndex;
504
575
 
@@ -515,7 +586,10 @@ export class SortableList extends RapidElement {
515
586
  }
516
587
 
517
588
  this.fireCustomEvent(CustomEventType.DragStop, {
518
- id: this.draggingId
589
+ id: this.draggingId,
590
+ isExternal: this.isExternalDrag,
591
+ mouseX: evt.clientX,
592
+ mouseY: evt.clientY
519
593
  });
520
594
 
521
595
  this.draggingId = null;
@@ -525,6 +599,7 @@ export class SortableList extends RapidElement {
525
599
  this.originalDragIndex = -1;
526
600
  this.pendingDropIndex = -1;
527
601
  this.pendingTargetElement = null;
602
+ this.isExternalDrag = false;
528
603
 
529
604
  // Clear the ghost reference since we removed it
530
605
  this.ghostElement = null;
@@ -808,12 +808,6 @@ export class ContactChat extends ContactStoreElement {
808
808
  };
809
809
  } else if (event._user) {
810
810
  return event._user;
811
- } else if (event.created_by) {
812
- return {
813
- email: event.created_by.email,
814
- name: `${event.created_by.first_name} ${event.created_by.last_name}`.trim(),
815
- avatar: event.created_by.avatar
816
- };
817
811
  }
818
812
  return null;
819
813
  }
@@ -852,13 +846,9 @@ export class ContactChat extends ContactStoreElement {
852
846
  } else if (
853
847
  event.type === 'msg_created' ||
854
848
  event.type === 'msg_received' ||
855
- event.type === 'ivr_created' ||
856
- event.type === 'broadcast_created'
849
+ event.type === 'ivr_created'
857
850
  ) {
858
851
  const msgEvent = event as MsgEvent;
859
- const status = msgEvent.status || msgEvent._status;
860
- const failedReason =
861
- msgEvent.failed_reason_display || msgEvent._failed_reason;
862
852
 
863
853
  messages.push({
864
854
  id: event.uuid,
@@ -867,7 +857,10 @@ export class ContactChat extends ContactStoreElement {
867
857
  date: new Date(msgEvent.created_on),
868
858
  attachments: msgEvent.msg.attachments,
869
859
  text: msgEvent.msg.text,
870
- sendError: status === 'E' || status === 'F',
860
+ sendError:
861
+ msgEvent._status &&
862
+ (msgEvent._status.status === 'errored' ||
863
+ msgEvent._status.status === 'failed'),
871
864
  popup: html`<div
872
865
  style="display: flex; flex-direction: row; align-items:center; justify-content: space-between;font-size:0.9em;line-height:1em;min-width:10em"
873
866
  >
@@ -882,20 +875,9 @@ export class ContactChat extends ContactStoreElement {
882
875
  ${msgEvent.optin.name}
883
876
  </div>`
884
877
  : null}
885
- ${failedReason
886
- ? html`
887
- <div
888
- style="margin-top:0.2em;margin-right: 0.5em;min-width:10em;max-width:15em;color:var(--color-error);font-size:0.9em"
889
- >
890
- ${failedReason}
891
- </div>
892
- `
893
- : null}
894
878
  </div>
895
- ${msgEvent.logs_url || msgEvent._logs_url
896
- ? html`<a
897
- style="margin-left:0.5em"
898
- href="${msgEvent.logs_url || msgEvent._logs_url}"
879
+ ${msgEvent._logs_url
880
+ ? html`<a style="margin-left:0.5em" href="${msgEvent._logs_url}"
899
881
  ><temba-icon name="log"></temba-icon
900
882
  ></a>`
901
883
  : null}
package/src/locales/es.ts CHANGED
@@ -1,13 +1,18 @@
1
- // Do not modify this file by hand!
2
- // Re-generate this file by running lit-localize
3
-
4
- /* eslint-disable no-irregular-whitespace */
5
- /* eslint-disable @typescript-eslint/no-explicit-any */
6
-
7
- export const templates = {
8
- scf1453991c986b25: `Tab para completar, enter para seleccionar`,
9
- s73b4d70c02f4b4e0: `No options`,
10
- s8f02e3a18ffc083a: `Are not currently in a flow`,
11
- s638236250662c6b3: `Have sent a message in the last`,
12
- s4788ee206c4570c7: `Have not started this flow in the last 90 days`
13
- };
1
+
2
+ // Do not modify this file by hand!
3
+ // Re-generate this file by running lit-localize
4
+
5
+
6
+
7
+
8
+ /* eslint-disable no-irregular-whitespace */
9
+ /* eslint-disable @typescript-eslint/no-explicit-any */
10
+
11
+ export const templates = {
12
+ 'scf1453991c986b25': `Tab para completar, enter para seleccionar`,
13
+ 's73b4d70c02f4b4e0': `No options`,
14
+ 's8f02e3a18ffc083a': `Are not currently in a flow`,
15
+ 's638236250662c6b3': `Have sent a message in the last`,
16
+ 's4788ee206c4570c7': `Have not started this flow in the last 90 days`,
17
+ };
18
+