@nyaruka/temba-components 0.129.8 → 0.129.10

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 (282) hide show
  1. package/CHANGELOG.md +37 -3
  2. package/demo/data/flows/sample-flow.json +186 -96
  3. package/demo/test-colorpicker.html +30 -0
  4. package/dist/temba-components.js +1126 -1111
  5. package/dist/temba-components.js.map +1 -1
  6. package/out-tsc/src/events.js.map +1 -1
  7. package/out-tsc/src/excellent/helpers.js +2 -2
  8. package/out-tsc/src/excellent/helpers.js.map +1 -1
  9. package/out-tsc/src/flow/CanvasNode.js +25 -7
  10. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  11. package/out-tsc/src/flow/Editor.js +11 -1
  12. package/out-tsc/src/flow/Editor.js.map +1 -1
  13. package/out-tsc/src/flow/NodeEditor.js +133 -290
  14. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  15. package/out-tsc/src/flow/actions/add_input_labels.js +40 -0
  16. package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
  17. package/out-tsc/src/flow/actions/call_llm.js +56 -3
  18. package/out-tsc/src/flow/actions/call_llm.js.map +1 -1
  19. package/out-tsc/src/flow/actions/call_webhook.js +1 -1
  20. package/out-tsc/src/flow/actions/call_webhook.js.map +1 -1
  21. package/out-tsc/src/flow/actions/open_ticket.js +65 -3
  22. package/out-tsc/src/flow/actions/open_ticket.js.map +1 -1
  23. package/out-tsc/src/flow/actions/set_run_result.js +75 -0
  24. package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
  25. package/out-tsc/src/flow/config.js +4 -0
  26. package/out-tsc/src/flow/config.js.map +1 -1
  27. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +227 -0
  28. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -0
  29. package/out-tsc/src/flow/nodes/split_by_ticket.js +18 -0
  30. package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -0
  31. package/out-tsc/src/flow/nodes/wait_for_response.js +27 -1
  32. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
  33. package/out-tsc/src/flow/types.js +0 -65
  34. package/out-tsc/src/flow/types.js.map +1 -1
  35. package/out-tsc/src/form/ArrayEditor.js +63 -117
  36. package/out-tsc/src/form/ArrayEditor.js.map +1 -1
  37. package/out-tsc/src/form/BaseListEditor.js +4 -3
  38. package/out-tsc/src/form/BaseListEditor.js.map +1 -1
  39. package/out-tsc/src/form/Checkbox.js +77 -24
  40. package/out-tsc/src/form/Checkbox.js.map +1 -1
  41. package/out-tsc/src/form/ColorPicker.js +28 -40
  42. package/out-tsc/src/form/ColorPicker.js.map +1 -1
  43. package/out-tsc/src/form/Completion.js +44 -53
  44. package/out-tsc/src/form/Completion.js.map +1 -1
  45. package/out-tsc/src/form/Compose.js +7 -8
  46. package/out-tsc/src/form/Compose.js.map +1 -1
  47. package/out-tsc/src/form/ContactSearch.js +3 -4
  48. package/out-tsc/src/form/ContactSearch.js.map +1 -1
  49. package/out-tsc/src/form/DatePicker.js +29 -36
  50. package/out-tsc/src/form/DatePicker.js.map +1 -1
  51. package/out-tsc/src/form/{FormField.js → FieldElement.js} +81 -53
  52. package/out-tsc/src/form/FieldElement.js.map +1 -0
  53. package/out-tsc/src/form/FieldRenderer.js +306 -0
  54. package/out-tsc/src/form/FieldRenderer.js.map +1 -0
  55. package/out-tsc/src/form/ImagePicker.js +122 -126
  56. package/out-tsc/src/form/ImagePicker.js.map +1 -1
  57. package/out-tsc/src/form/KeyValueEditor.js +41 -37
  58. package/out-tsc/src/form/KeyValueEditor.js.map +1 -1
  59. package/out-tsc/src/form/MessageEditor.js +55 -63
  60. package/out-tsc/src/form/MessageEditor.js.map +1 -1
  61. package/out-tsc/src/form/TembaSlider.js +3 -3
  62. package/out-tsc/src/form/TembaSlider.js.map +1 -1
  63. package/out-tsc/src/form/TemplateEditor.js +3 -3
  64. package/out-tsc/src/form/TemplateEditor.js.map +1 -1
  65. package/out-tsc/src/form/TextInput.js +23 -27
  66. package/out-tsc/src/form/TextInput.js.map +1 -1
  67. package/out-tsc/src/form/select/Select.js +57 -35
  68. package/out-tsc/src/form/select/Select.js.map +1 -1
  69. package/out-tsc/src/form/select/UserSelect.js +8 -9
  70. package/out-tsc/src/form/select/UserSelect.js.map +1 -1
  71. package/out-tsc/src/form/select/WorkspaceSelect.js +7 -8
  72. package/out-tsc/src/form/select/WorkspaceSelect.js.map +1 -1
  73. package/out-tsc/src/live/ContactChat.js +62 -44
  74. package/out-tsc/src/live/ContactChat.js.map +1 -1
  75. package/out-tsc/src/live/ContactFieldEditor.js.map +1 -1
  76. package/out-tsc/src/markdown.js +13 -11
  77. package/out-tsc/src/markdown.js.map +1 -1
  78. package/out-tsc/temba-modules.js +3 -2
  79. package/out-tsc/temba-modules.js.map +1 -1
  80. package/out-tsc/test/ActionHelper.js +2 -0
  81. package/out-tsc/test/ActionHelper.js.map +1 -1
  82. package/out-tsc/test/NodeHelper.js +148 -0
  83. package/out-tsc/test/NodeHelper.js.map +1 -0
  84. package/out-tsc/test/actions/call_llm.test.js +103 -0
  85. package/out-tsc/test/actions/call_llm.test.js.map +1 -0
  86. package/out-tsc/test/nodes/split_by_llm_categorize.test.js +532 -0
  87. package/out-tsc/test/nodes/split_by_llm_categorize.test.js.map +1 -0
  88. package/out-tsc/test/nodes/split_by_random.test.js +150 -0
  89. package/out-tsc/test/nodes/split_by_random.test.js.map +1 -0
  90. package/out-tsc/test/nodes/wait_for_digits.test.js +150 -0
  91. package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -0
  92. package/out-tsc/test/nodes/wait_for_response.test.js +171 -0
  93. package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -0
  94. package/out-tsc/test/temba-add-input-labels.test.js +70 -0
  95. package/out-tsc/test/temba-add-input-labels.test.js.map +1 -0
  96. package/out-tsc/test/temba-checkbox.test.js +16 -0
  97. package/out-tsc/test/temba-checkbox.test.js.map +1 -1
  98. package/out-tsc/test/temba-field-renderer.test.js +296 -0
  99. package/out-tsc/test/temba-field-renderer.test.js.map +1 -0
  100. package/out-tsc/test/temba-integration-markdown.test.js +2 -4
  101. package/out-tsc/test/temba-integration-markdown.test.js.map +1 -1
  102. package/out-tsc/test/temba-markdown.test.js +1 -1
  103. package/out-tsc/test/temba-markdown.test.js.map +1 -1
  104. package/out-tsc/test/temba-node-editor.test.js +400 -0
  105. package/out-tsc/test/temba-node-editor.test.js.map +1 -1
  106. package/out-tsc/test/temba-select.test.js +6 -3
  107. package/out-tsc/test/temba-select.test.js.map +1 -1
  108. package/out-tsc/test/temba-slider.test.js +0 -1
  109. package/out-tsc/test/temba-slider.test.js.map +1 -1
  110. package/out-tsc/test/temba-webchat.test.js +1 -1
  111. package/out-tsc/test/temba-webchat.test.js.map +1 -1
  112. package/package.json +1 -1
  113. package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
  114. package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
  115. package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
  116. package/screenshots/truth/actions/call_llm/editor/information-extraction.png +0 -0
  117. package/screenshots/truth/actions/call_llm/editor/sentiment-analysis.png +0 -0
  118. package/screenshots/truth/actions/call_llm/editor/summarization.png +0 -0
  119. package/screenshots/truth/actions/call_llm/editor/translation-task.png +0 -0
  120. package/screenshots/truth/actions/call_llm/render/information-extraction.png +0 -0
  121. package/screenshots/truth/actions/call_llm/render/sentiment-analysis.png +0 -0
  122. package/screenshots/truth/actions/call_llm/render/summarization.png +0 -0
  123. package/screenshots/truth/actions/call_llm/render/translation-task.png +0 -0
  124. package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
  125. package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
  126. package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
  127. package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
  128. package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
  129. package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
  130. package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
  131. package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
  132. package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
  133. package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
  134. package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
  135. package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
  136. package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
  137. package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
  138. package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
  139. package/screenshots/truth/checkbox/checkbox-label-background-hover.png +0 -0
  140. package/screenshots/truth/checkbox/checkbox-no-label-no-background-hover.png +0 -0
  141. package/screenshots/truth/checkbox/checkbox-with-help-text.png +0 -0
  142. package/screenshots/truth/checkbox/checked.png +0 -0
  143. package/screenshots/truth/checkbox/default.png +0 -0
  144. package/screenshots/truth/colorpicker/default.png +0 -0
  145. package/screenshots/truth/colorpicker/focused.png +0 -0
  146. package/screenshots/truth/colorpicker/initialized.png +0 -0
  147. package/screenshots/truth/colorpicker/selected.png +0 -0
  148. package/screenshots/truth/editor/router.png +0 -0
  149. package/screenshots/truth/editor/send_msg.png +0 -0
  150. package/screenshots/truth/editor/set_contact_language.png +0 -0
  151. package/screenshots/truth/editor/set_contact_name.png +0 -0
  152. package/screenshots/truth/editor/set_run_result.png +0 -0
  153. package/screenshots/truth/editor/wait.png +0 -0
  154. package/screenshots/truth/field-renderer/checkbox-checked.png +0 -0
  155. package/screenshots/truth/field-renderer/checkbox-unchecked.png +0 -0
  156. package/screenshots/truth/field-renderer/checkbox-with-errors.png +0 -0
  157. package/screenshots/truth/field-renderer/context-comparison.png +0 -0
  158. package/screenshots/truth/field-renderer/key-value-with-label.png +0 -0
  159. package/screenshots/truth/field-renderer/message-editor-with-label.png +0 -0
  160. package/screenshots/truth/field-renderer/select-multi.png +0 -0
  161. package/screenshots/truth/field-renderer/select-no-label.png +0 -0
  162. package/screenshots/truth/field-renderer/select-with-label.png +0 -0
  163. package/screenshots/truth/field-renderer/text-evaluated.png +0 -0
  164. package/screenshots/truth/field-renderer/text-no-label.png +0 -0
  165. package/screenshots/truth/field-renderer/text-with-errors.png +0 -0
  166. package/screenshots/truth/field-renderer/text-with-label.png +0 -0
  167. package/screenshots/truth/field-renderer/textarea-evaluated.png +0 -0
  168. package/screenshots/truth/field-renderer/textarea-with-label.png +0 -0
  169. package/screenshots/truth/integration/checkbox-markdown-errors.png +0 -0
  170. package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
  171. package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
  172. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  173. package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
  174. package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
  175. package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
  176. package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
  177. package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
  178. package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
  179. package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
  180. package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
  181. package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
  182. package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
  183. package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
  184. package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
  185. package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
  186. package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
  187. package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
  188. package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
  189. package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
  190. package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
  191. package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
  192. package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
  193. package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
  194. package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
  195. package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
  196. package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
  197. package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
  198. package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
  199. package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
  200. package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
  201. package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
  202. package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
  203. package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
  204. package/screenshots/truth/omnibox/selected.png +0 -0
  205. package/screenshots/truth/run-list/basic.png +0 -0
  206. package/screenshots/truth/select/functions.png +0 -0
  207. package/screenshots/truth/select/multi-with-endpoint.png +0 -0
  208. package/screenshots/truth/select/search-enabled.png +0 -0
  209. package/src/events.ts +12 -6
  210. package/src/excellent/helpers.ts +2 -2
  211. package/src/flow/CanvasNode.ts +22 -1
  212. package/src/flow/Editor.ts +12 -1
  213. package/src/flow/NodeEditor.ts +186 -374
  214. package/src/flow/actions/add_input_labels.ts +45 -0
  215. package/src/flow/actions/call_llm.ts +57 -3
  216. package/src/flow/actions/call_webhook.ts +1 -1
  217. package/src/flow/actions/open_ticket.ts +74 -3
  218. package/src/flow/actions/set_run_result.ts +83 -0
  219. package/src/flow/config.ts +4 -0
  220. package/src/flow/nodes/split_by_llm_categorize.ts +277 -0
  221. package/src/flow/nodes/split_by_ticket.ts +19 -0
  222. package/src/flow/nodes/wait_for_response.ts +28 -1
  223. package/src/flow/types.ts +26 -127
  224. package/src/form/ArrayEditor.ts +79 -139
  225. package/src/form/BaseListEditor.ts +4 -4
  226. package/src/form/Checkbox.ts +81 -24
  227. package/src/form/ColorPicker.ts +31 -43
  228. package/src/form/Completion.ts +49 -56
  229. package/src/form/Compose.ts +8 -8
  230. package/src/form/ContactSearch.ts +3 -4
  231. package/src/form/DatePicker.ts +32 -38
  232. package/src/form/{FormField.ts → FieldElement.ts} +108 -55
  233. package/src/form/FieldRenderer.ts +466 -0
  234. package/src/form/ImagePicker.ts +107 -110
  235. package/src/form/KeyValueEditor.ts +43 -39
  236. package/src/form/MessageEditor.ts +61 -67
  237. package/src/form/TembaSlider.ts +3 -3
  238. package/src/form/TemplateEditor.ts +3 -3
  239. package/src/form/TextInput.ts +26 -29
  240. package/src/form/select/Select.ts +63 -37
  241. package/src/form/select/UserSelect.ts +10 -11
  242. package/src/form/select/WorkspaceSelect.ts +9 -10
  243. package/src/live/ContactChat.ts +62 -47
  244. package/src/live/ContactFieldEditor.ts +2 -2
  245. package/src/markdown.ts +19 -11
  246. package/src/store/flow-definition.d.ts +5 -2
  247. package/static/api/labels.json +31 -0
  248. package/static/api/topics.json +24 -9
  249. package/static/api/users.json +35 -16
  250. package/static/css/temba-components.css +3 -3
  251. package/stress-test.js +18 -13
  252. package/temba-modules.ts +3 -2
  253. package/test/ActionHelper.ts +2 -0
  254. package/test/NodeHelper.ts +184 -0
  255. package/test/actions/call_llm.test.ts +137 -0
  256. package/test/nodes/README.md +78 -0
  257. package/test/nodes/split_by_llm_categorize.test.ts +698 -0
  258. package/test/nodes/split_by_random.test.ts +177 -0
  259. package/test/nodes/wait_for_digits.test.ts +176 -0
  260. package/test/nodes/wait_for_response.test.ts +206 -0
  261. package/test/temba-add-input-labels.test.ts +87 -0
  262. package/test/temba-checkbox.test.ts +26 -0
  263. package/test/temba-field-renderer.test.ts +482 -0
  264. package/test/temba-integration-markdown.test.ts +2 -4
  265. package/test/temba-markdown.test.ts +1 -1
  266. package/test/temba-node-editor.test.ts +496 -0
  267. package/test/temba-select.test.ts +6 -6
  268. package/test/temba-slider.test.ts +0 -1
  269. package/test/temba-webchat.test.ts +1 -1
  270. package/test-assets/contacts/history.json +7 -20
  271. package/test-assets/select/llms.json +18 -0
  272. package/web-dev-mock.mjs +96 -6
  273. package/web-dev-server.config.mjs +29 -7
  274. package/out-tsc/src/form/FormElement.js +0 -67
  275. package/out-tsc/src/form/FormElement.js.map +0 -1
  276. package/out-tsc/src/form/FormField.js.map +0 -1
  277. package/out-tsc/test/temba-formfield.test.js +0 -94
  278. package/out-tsc/test/temba-formfield.test.js.map +0 -1
  279. package/src/form/FormElement.ts +0 -69
  280. package/test/temba-flow-editor.test.ts.backup +0 -563
  281. package/test/temba-formfield.test.ts +0 -121
  282. package/test/temba-utils-index.test.ts.backup +0 -1737
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable @typescript-eslint/no-empty-function */
2
- import { TemplateResult, html, css, CSSResult, CSSResultArray } from 'lit';
2
+ import { TemplateResult, html, css } from 'lit';
3
3
  import { property, state } from 'lit/decorators.js';
4
4
  import {
5
5
  getUrl,
@@ -11,7 +11,7 @@ import {
11
11
  import '../../display/Options';
12
12
  import '../../list/SortableList';
13
13
  import { EventHandler } from '../../RapidElement';
14
- import { FormElement } from '../../form/FormElement';
14
+ import { FieldElement } from '../../form/FieldElement';
15
15
 
16
16
  import { lru } from 'tiny-lru';
17
17
  import { CompletionOption, CustomEventType, Position } from '../../interfaces';
@@ -35,11 +35,13 @@ export interface SelectOption {
35
35
  arbitrary?: boolean;
36
36
  }
37
37
 
38
- export class Select<T extends SelectOption> extends FormElement {
38
+ export class Select<T extends SelectOption> extends FieldElement {
39
39
  private hiddenInputs: HTMLInputElement[] = [];
40
40
 
41
- static get styles(): CSSResult | CSSResultArray {
41
+ static get styles() {
42
42
  return css`
43
+ ${super.styles}
44
+
43
45
  :host {
44
46
  --transition-speed: 0;
45
47
  font-family: var(--font-family);
@@ -222,6 +224,7 @@ export class Select<T extends SelectOption> extends FormElement {
222
224
 
223
225
  .multi temba-sortable-list {
224
226
  margin: 0 !important;
227
+ flex-grow: 1;
225
228
  }
226
229
 
227
230
  input {
@@ -255,6 +258,12 @@ export class Select<T extends SelectOption> extends FormElement {
255
258
 
256
259
  .multi .input-wrapper {
257
260
  margin-left: 2px !important;
261
+ margin-right: 2px !important;
262
+ margin-top: 2px;
263
+ margin-bottom: 2px;
264
+ flex-shrink: 0;
265
+ min-width: 100px;
266
+ align-self: center;
258
267
  }
259
268
 
260
269
  .input-wrapper:focus-within .placeholder {
@@ -290,11 +299,6 @@ export class Select<T extends SelectOption> extends FormElement {
290
299
  box-shadow: none !important;
291
300
  }
292
301
 
293
- .multi .input-wrapper {
294
- flex-shrink: 0;
295
- min-width: 100px;
296
- }
297
-
298
302
  .input-wrapper .searchbox {
299
303
  }
300
304
 
@@ -310,6 +314,17 @@ export class Select<T extends SelectOption> extends FormElement {
310
314
  margin-left: 6px;
311
315
  }
312
316
 
317
+ .empty .placeholder {
318
+ display: block;
319
+ }
320
+
321
+ .multi .placeholder {
322
+ display: block;
323
+ margin: 2px 2px;
324
+ padding: 2px 8px;
325
+ align-self: center;
326
+ }
327
+
313
328
  .footer {
314
329
  padding: 5px 10px;
315
330
  background: var(--color-primary-light);
@@ -322,7 +337,7 @@ export class Select<T extends SelectOption> extends FormElement {
322
337
  .small {
323
338
  --temba-select-selected-padding: 6px;
324
339
  --temba-select-selected-line-height: 12px;
325
- --temba-select-selected-font-size: 12px;
340
+ --temba-select-selected-font-size: 14px;
326
341
  --temba-select-min-height: 2.28em;
327
342
  }
328
343
 
@@ -476,15 +491,26 @@ export class Select<T extends SelectOption> extends FormElement {
476
491
  @property({ type: String, attribute: 'info_text' })
477
492
  infoText = '';
478
493
 
494
+ // Override the setter to ensure values is always an array
479
495
  @property({ type: Array })
480
- values: T[] = [];
496
+ set values(newValues: any) {
497
+ this._values = Array.isArray(newValues) ? newValues : [];
498
+ this.requestUpdate('values');
499
+ }
500
+
501
+ get values(): T[] {
502
+ return this._values || [];
503
+ }
504
+
505
+ private _values: T[] = [];
481
506
 
482
507
  @property({ type: Object })
483
508
  selection: any;
484
509
 
485
510
  @property({ attribute: false })
486
- getName: (option: any) => string = (option: any) =>
487
- option[this.nameKey || 'name'];
511
+ getName: (option: any) => string = (option: any) => {
512
+ return option[this.nameKey || 'name'];
513
+ };
488
514
 
489
515
  @property({ attribute: false })
490
516
  isMatch: (option: any, q: string) => boolean = this.isMatchDefault;
@@ -545,7 +571,7 @@ export class Select<T extends SelectOption> extends FormElement {
545
571
  private alphaSort = (a: any, b: any) => {
546
572
  // by default, all endpoint values are sorted by name
547
573
  if (this.endpoint) {
548
- return this.getName(a).localeCompare(this.getName(b));
574
+ return this.getNameInternal(a).localeCompare(this.getNameInternal(b));
549
575
  }
550
576
  return 0;
551
577
  };
@@ -573,7 +599,7 @@ export class Select<T extends SelectOption> extends FormElement {
573
599
  }
574
600
 
575
601
  public isMatchDefault(option: T, q: string) {
576
- const name = this.getName(option) || '';
602
+ const name = this.getNameInternal(option) || '';
577
603
  return name.toLowerCase().indexOf(q) > -1;
578
604
  }
579
605
 
@@ -963,7 +989,9 @@ export class Select<T extends SelectOption> extends FormElement {
963
989
  }
964
990
 
965
991
  protected getNameInternal: (option: T) => string = (option: T) => {
966
- return this.getName(option);
992
+ return this.getName
993
+ ? this.getName(option)
994
+ : option[this.nameKey || 'name'] || '';
967
995
  };
968
996
 
969
997
  private getOptionsDefault(response: WebResponse): any[] {
@@ -1493,7 +1521,7 @@ export class Select<T extends SelectOption> extends FormElement {
1493
1521
  name="${icon}"
1494
1522
  style="margin-right:0.5em;"
1495
1523
  ></temba-icon>`
1496
- : null}<span>${this.getName(option)}</span>
1524
+ : null}<span>${this.getNameInternal(option)}</span>
1497
1525
  </div>
1498
1526
  `;
1499
1527
  }
@@ -1634,11 +1662,15 @@ export class Select<T extends SelectOption> extends FormElement {
1634
1662
  }
1635
1663
  }
1636
1664
 
1637
- public render(): TemplateResult {
1665
+ protected renderWidget(): TemplateResult {
1638
1666
  const placeholder = this.values.length === 0 ? this.placeholder : '';
1639
- const placeholderDiv = html`
1640
- <div class="placeholder">${placeholder}</div>
1641
- `;
1667
+
1668
+ // Single unified placeholder - shows when empty and (not focused OR not searchable)
1669
+ const shouldShowPlaceholder =
1670
+ this.values.length === 0 && (!this.focused || !this.searchable);
1671
+ const placeholderElement = shouldShowPlaceholder
1672
+ ? html`<div class="placeholder">${placeholder}</div>`
1673
+ : null;
1642
1674
 
1643
1675
  const clear =
1644
1676
  this.clearable && this.values.length > 0 && !this.isMultiMode
@@ -1683,10 +1715,9 @@ export class Select<T extends SelectOption> extends FormElement {
1683
1715
  .value=${this.input}
1684
1716
  />
1685
1717
  <div id="anchor" style=${styleMap(anchorStyles)}></div>
1686
- ${placeholderDiv}
1687
1718
  </div>
1688
1719
  `
1689
- : placeholderDiv;
1720
+ : null;
1690
1721
 
1691
1722
  const items = html`${!this.isMultiMode && !this.resolving ? input : null}
1692
1723
  ${this.isMultiMode && this.values.length > 1
@@ -1760,9 +1791,10 @@ export class Select<T extends SelectOption> extends FormElement {
1760
1791
  </div>
1761
1792
  `
1762
1793
  )}
1794
+ ${this.searchable && this.focused ? input : null}
1763
1795
  </temba-sortable-list>
1764
1796
  `
1765
- : this.values.map(
1797
+ : html`${this.values.map(
1766
1798
  (selected: any, index: number) => html`
1767
1799
  <div
1768
1800
  class="selected-item ${index === this.selectedIndex
@@ -1811,19 +1843,10 @@ export class Select<T extends SelectOption> extends FormElement {
1811
1843
  </div>
1812
1844
  `
1813
1845
  )}
1814
- ${this.isMultiMode ? input : null}`;
1846
+ ${this.isMultiMode && this.searchable && this.focused ? input : null}
1847
+ ${placeholderElement}`}`;
1815
1848
 
1816
1849
  return html`
1817
-
1818
- <temba-field
1819
- name=${this.name}
1820
- .label=${this.label}
1821
- .helpText=${this.helpText}
1822
- .errors=${this.errors}
1823
- .widgetOnly=${this.widgetOnly}
1824
- .hideErrors=${this.hideErrors}
1825
- ?disabled=${this.disabled}
1826
- >
1827
1850
  <slot></slot>
1828
1851
  <div class="wrapper-bg">
1829
1852
 
@@ -1917,7 +1940,10 @@ export class Select<T extends SelectOption> extends FormElement {
1917
1940
  : null
1918
1941
  }
1919
1942
  </temba-options>
1920
- </temba-field>
1921
- `;
1943
+ `;
1944
+ }
1945
+
1946
+ public render(): TemplateResult {
1947
+ return this.renderField();
1922
1948
  }
1923
1949
  }
@@ -1,4 +1,4 @@
1
- import { css, CSSResultArray, html, TemplateResult } from 'lit';
1
+ import { css, html, TemplateResult } from 'lit';
2
2
  import { Select, SelectOption } from './Select';
3
3
  import { property } from 'lit/decorators.js';
4
4
  import { getFullName } from '../../display/TembaUser';
@@ -10,16 +10,15 @@ export interface UserOption extends SelectOption {
10
10
  }
11
11
 
12
12
  export class UserSelect extends Select<UserOption> {
13
- static get styles(): CSSResultArray {
14
- return [
15
- super.styles,
16
- css`
17
- :host {
18
- width: 150px;
19
- display: block;
20
- }
21
- `
22
- ];
13
+ static get styles() {
14
+ return css`
15
+ ${super.styles}
16
+
17
+ :host {
18
+ width: 150px;
19
+ display: block;
20
+ }
21
+ `;
23
22
  }
24
23
 
25
24
  @property({ type: String })
@@ -1,4 +1,4 @@
1
- import { css, CSSResultArray, html, TemplateResult } from 'lit';
1
+ import { css, html, TemplateResult } from 'lit';
2
2
  import { Select, SelectOption } from './Select';
3
3
  import { property } from 'lit/decorators.js';
4
4
  import { getScrollParent } from '../../utils';
@@ -10,15 +10,14 @@ export interface WorkspaceOption extends SelectOption {
10
10
  }
11
11
 
12
12
  export class WorkspaceSelect extends Select<WorkspaceOption> {
13
- static get styles(): CSSResultArray {
14
- return [
15
- super.styles,
16
- css`
17
- :host {
18
- border: 0px solid blue;
19
- }
20
- `
21
- ];
13
+ static get styles() {
14
+ return css`
15
+ ${super.styles}
16
+
17
+ :host {
18
+ border: 0px solid blue;
19
+ }
20
+ `;
22
21
  }
23
22
 
24
23
  @property({ type: String })
@@ -20,15 +20,16 @@ import { ContactStoreElement } from './ContactStoreElement';
20
20
  import { Compose, ComposeValue } from '../form/Compose';
21
21
  import {
22
22
  AirtimeTransferredEvent,
23
+ CallEvent,
23
24
  ChannelEvent,
25
+ ChatStartedEvent,
24
26
  ContactEvent,
25
27
  ContactGroupsEvent,
26
28
  ContactHistoryPage,
27
29
  ContactLanguageChangedEvent,
28
- FlowEvent,
29
30
  MsgEvent,
30
31
  NameChangedEvent,
31
- OptinRequestedEvent,
32
+ OptInEvent,
32
33
  RunEvent,
33
34
  TicketEvent,
34
35
  UpdateFieldEvent,
@@ -50,32 +51,35 @@ export const BODY_SNIPPET_LENGTH = 250;
50
51
  */
51
52
 
52
53
  export enum Events {
53
- MESSAGE_CREATED = 'msg_created',
54
- MESSAGE_RECEIVED = 'msg_received',
54
+ AIRTIME_TRANSFERRED = 'airtime_transferred',
55
55
  BROADCAST_CREATED = 'broadcast_created',
56
- IVR_CREATED = 'ivr_created',
56
+ CALL_CREATED = 'call_created',
57
+ CALL_MISSED = 'call_missed',
58
+ CALL_RECEIVED = 'call_received',
59
+ CHAT_STARTED = 'chat_started',
57
60
  CONTACT_FIELD_CHANGED = 'contact_field_changed',
58
61
  CONTACT_GROUPS_CHANGED = 'contact_groups_changed',
62
+ CONTACT_LANGUAGE_CHANGED = 'contact_language_changed',
59
63
  CONTACT_NAME_CHANGED = 'contact_name_changed',
60
64
  CONTACT_URNS_CHANGED = 'contact_urns_changed',
61
- CHANNEL_EVENT = 'channel_event',
62
- CONTACT_LANGUAGE_CHANGED = 'contact_language_changed',
63
- AIRTIME_TRANSFERRED = 'airtime_transferred',
64
- CALL_STARTED = 'call_started',
65
+ IVR_CREATED = 'ivr_created',
66
+ MSG_CREATED = 'msg_created',
67
+ MSG_RECEIVED = 'msg_received',
65
68
  NOTE_CREATED = 'note_created',
69
+ OPTIN_REQUESTED = 'optin_requested',
70
+ OPTIN_STARTED = 'optin_started',
71
+ OPTIN_STOPPED = 'optin_stopped',
72
+ RUN_ENDED = 'run_ended',
73
+ RUN_STARTED = 'run_started',
66
74
  TICKET_ASSIGNED = 'ticket_assigned',
67
- TICKET_NOTE_ADDED = 'ticket_note_added',
68
75
  TICKET_CLOSED = 'ticket_closed',
76
+ TICKET_NOTE_ADDED = 'ticket_note_added',
69
77
  TICKET_OPENED = 'ticket_opened',
70
78
  TICKET_REOPENED = 'ticket_reopened',
71
79
  TICKET_TOPIC_CHANGED = 'ticket_topic_changed',
72
- OPTIN_REQUESTED = 'optin_requested',
73
- RUN_STARTED = 'run_started',
74
- RUN_ENDED = 'run_ended',
75
80
 
76
81
  // deprecated
77
- FLOW_ENTERED = 'flow_entered',
78
- FLOW_EXITED = 'flow_exited'
82
+ CHANNEL_EVENT = 'channel_event'
79
83
  }
80
84
 
81
85
  const renderInfoList = (singular: string, plural: string, items: any[]) => {
@@ -108,28 +112,16 @@ const renderChannelEvent = (event: ChannelEvent): string => {
108
112
  } else if (event.event.type === 'stop_contact') {
109
113
  return 'Stopped';
110
114
  } else if (event.event.type === 'mt_call') {
111
- return 'Outgoing Phone Call';
115
+ return 'Outgoing Phone Call'; // deprecated
112
116
  } else if (event.event.type == 'mo_call') {
113
- return 'Incoming Phone call';
117
+ return 'Incoming Phone call'; // deprecated
114
118
  } else if (event.event.type == 'optin') {
115
- return `Opted in to **${event.event.optin?.name}**`;
119
+ return `Opted in to **${event.event.optin?.name}**`; // deprecated
116
120
  } else if (event.event.type == 'optout') {
117
- return `Opted out of **${event.event.optin?.name}**`;
121
+ return `Opted out of **${event.event.optin?.name}**`; // deprecated
118
122
  }
119
123
  };
120
124
 
121
- const renderFlowEvent = (event: FlowEvent): string => {
122
- let verb = 'Interrupted';
123
- if (event.status !== 'I') {
124
- if (event.type === Events.FLOW_ENTERED) {
125
- verb = 'Started';
126
- } else {
127
- verb = 'Completed';
128
- }
129
- }
130
- return `${verb} [**${event.flow.name}**](/flow/editor/${event.flow.uuid}/)`;
131
- };
132
-
133
125
  const renderRunEvent = (event: RunEvent): string => {
134
126
  let verb = 'Started';
135
127
  if (event.type === Events.RUN_ENDED) {
@@ -145,6 +137,14 @@ const renderRunEvent = (event: RunEvent): string => {
145
137
  return `${verb} [**${event.flow.name}**](/flow/editor/${event.flow.uuid}/)`;
146
138
  };
147
139
 
140
+ const renderChatStartedEvent = (event: ChatStartedEvent): string => {
141
+ if (event.params) {
142
+ return `Chat referral`;
143
+ } else {
144
+ return `Chat started`;
145
+ }
146
+ };
147
+
148
148
  const renderUpdateEvent = (event: UpdateFieldEvent): string => {
149
149
  return event.value
150
150
  ? `Updated **${event.field.name}** to **${event.value.text}**`
@@ -214,18 +214,30 @@ export const renderAirtimeTransferredEvent = (
214
214
  return `Transferred **${event.amount}** ${event.currency} of airtime`;
215
215
  };
216
216
 
217
- export const renderCallStartedEvent = (): string => {
218
- return `Call Started`;
219
- };
220
-
221
217
  export const renderContactLanguageChangedEvent = (
222
218
  event: ContactLanguageChangedEvent
223
219
  ): string => {
224
220
  return `Language updated to **${event.language}**`;
225
221
  };
226
222
 
227
- export const renderOptinRequested = (event: OptinRequestedEvent): string => {
228
- return `Requested opt-in for ${event.optin.name}`;
223
+ export const renderCallEvent = (event: CallEvent): string => {
224
+ if (event.type === Events.CALL_CREATED) {
225
+ return `Call started`;
226
+ } else if (event.type === Events.CALL_MISSED) {
227
+ return `Call missed`;
228
+ } else if (event.type === Events.CALL_RECEIVED) {
229
+ return `Call answered`;
230
+ }
231
+ };
232
+
233
+ export const renderOptInEvent = (event: OptInEvent): string => {
234
+ if (event.type === Events.OPTIN_REQUESTED) {
235
+ return `Requested opt-in for ${event.optin.name}`;
236
+ } else if (event.type === Events.OPTIN_STARTED) {
237
+ return `Opted in to **${event.optin.name}**`;
238
+ } else if (event.type === Events.OPTIN_STOPPED) {
239
+ return `Opted out of **${event.optin.name}**`;
240
+ }
229
241
  };
230
242
 
231
243
  export class ContactChat extends ContactStoreElement {
@@ -650,13 +662,6 @@ export class ContactChat extends ContactStoreElement {
650
662
  text: `Topic changed to **${(event as TicketEvent).topic.name}**`
651
663
  };
652
664
  break;
653
- case Events.FLOW_ENTERED:
654
- case Events.FLOW_EXITED:
655
- message = {
656
- type: MessageType.Inline,
657
- text: renderFlowEvent(event as FlowEvent)
658
- };
659
- break;
660
665
  case Events.RUN_STARTED:
661
666
  case Events.RUN_ENDED:
662
667
  message = {
@@ -694,10 +699,12 @@ export class ContactChat extends ContactStoreElement {
694
699
  text: renderAirtimeTransferredEvent(event as AirtimeTransferredEvent)
695
700
  };
696
701
  break;
697
- case Events.CALL_STARTED:
702
+ case Events.CALL_CREATED:
703
+ case Events.CALL_MISSED:
704
+ case Events.CALL_RECEIVED:
698
705
  message = {
699
706
  type: MessageType.Inline,
700
- text: renderCallStartedEvent()
707
+ text: renderCallEvent(event as CallEvent)
701
708
  };
702
709
  break;
703
710
  case Events.CHANNEL_EVENT:
@@ -706,6 +713,12 @@ export class ContactChat extends ContactStoreElement {
706
713
  text: renderChannelEvent(event as ChannelEvent)
707
714
  };
708
715
  break;
716
+ case Events.CHAT_STARTED:
717
+ message = {
718
+ type: MessageType.Inline,
719
+ text: renderChatStartedEvent(event as ChatStartedEvent)
720
+ };
721
+ break;
709
722
  case Events.CONTACT_LANGUAGE_CHANGED:
710
723
  message = {
711
724
  type: MessageType.Inline,
@@ -715,9 +728,11 @@ export class ContactChat extends ContactStoreElement {
715
728
  };
716
729
  break;
717
730
  case Events.OPTIN_REQUESTED:
731
+ case Events.OPTIN_STARTED:
732
+ case Events.OPTIN_STOPPED:
718
733
  message = {
719
734
  type: MessageType.Inline,
720
- text: renderOptinRequested(event as OptinRequestedEvent)
735
+ text: renderOptInEvent(event as OptInEvent)
721
736
  };
722
737
  break;
723
738
  }
@@ -1,12 +1,12 @@
1
1
  import { css, html, TemplateResult } from 'lit';
2
2
  import { property } from 'lit/decorators.js';
3
- import { FormElement } from '../form/FormElement';
4
3
  import { CustomEventType } from '../interfaces';
5
4
  import { RapidElement } from '../RapidElement';
6
5
  import { InputType, TextInput } from '../form/TextInput';
7
6
  import { Icon } from '../Icons';
8
7
  import { getClasses, WebResponse } from '../utils';
9
8
  import { Select } from '../form/select/Select';
9
+ import { FieldElement } from '../form/FieldElement';
10
10
 
11
11
  enum Status {
12
12
  Success = 'success',
@@ -380,7 +380,7 @@ export class ContactFieldEditor extends RapidElement {
380
380
  public handleSubmit() {
381
381
  const input = this.shadowRoot.querySelector(
382
382
  'temba-textinput, temba-datepicker'
383
- ) as FormElement;
383
+ ) as FieldElement;
384
384
 
385
385
  if (input.value !== this.value) {
386
386
  this.dirty = true;
package/src/markdown.ts CHANGED
@@ -11,31 +11,39 @@ import { Remarkable } from 'remarkable';
11
11
 
12
12
  export const markdown = new Remarkable();
13
13
 
14
- // Class-based directive API
15
- export class RenderMarkdown extends Directive {
16
- // State stored in class field
17
- // value: string | undefined;
14
+ // Base class for markdown rendering directives
15
+ abstract class BaseMarkdownDirective extends Directive {
18
16
  constructor(partInfo: PartInfo) {
19
17
  super(partInfo);
20
18
  // When necessary, validate part in constructor using `part.type`
21
19
  if (partInfo.type !== PartType.CHILD) {
22
- throw new Error('renderMarkdown only supports child expressions');
20
+ throw new Error('markdown directives only support child expressions');
23
21
  }
24
22
  }
23
+
25
24
  // Optional: override update to perform any direct DOM manipulation
26
- // DirectiveParameters<this>
27
25
  update(part: Part, [initialValue]: any) {
28
26
  /* Any imperative updates to DOM/parts would go here */
29
27
  return this.render(initialValue);
30
28
  }
31
- // Do SSR-compatible rendering (arguments are passed from call site)
29
+
30
+ // Abstract method to be implemented by subclasses
31
+ abstract render(initialValue: string): any;
32
+ }
33
+
34
+ // Class-based directive for block markdown rendering
35
+ export class RenderMarkdown extends BaseMarkdownDirective {
32
36
  render(initialValue: string) {
33
- // Previous state available on class field
34
- // if (this.value === undefined) {
35
- // this.value = initialValue;
36
- //}
37
37
  return html`${unsafeHTML(markdown.render(initialValue))}`;
38
38
  }
39
39
  }
40
40
 
41
+ // Class-based directive for inline markdown rendering
42
+ export class RenderMarkdownInline extends BaseMarkdownDirective {
43
+ render(initialValue: string) {
44
+ return html`${unsafeHTML(markdown.renderInline(initialValue))}`;
45
+ }
46
+ }
47
+
41
48
  export const renderMarkdown = directive(RenderMarkdown);
49
+ export const renderMarkdownInline = directive(RenderMarkdownInline);
@@ -44,6 +44,7 @@ export type ActionType =
44
44
  | 'split_by_subflow'
45
45
  | 'split_by_webhook'
46
46
  | 'split_by_llm'
47
+ | 'split_by_llm_categorize'
47
48
  | 'wait_for_response'
48
49
  | 'wait_for_menu'
49
50
  | 'wait_for_dial'
@@ -173,12 +174,14 @@ export interface CallResthook extends Action {
173
174
  export interface CallLLM extends Action {
174
175
  llm: NamedObject;
175
176
  instructions: string;
177
+ input: string;
176
178
  result_name: string;
177
179
  }
178
180
 
179
181
  export interface OpenTicket extends Action {
180
- subject: string;
181
- body: string;
182
+ subject?: string;
183
+ body?: string;
184
+ note?: string;
182
185
  assignee?: NamedObject;
183
186
  topic?: NamedObject;
184
187
  }
@@ -0,0 +1,31 @@
1
+ {
2
+ "next": null,
3
+ "previous": null,
4
+ "results": [
5
+ {
6
+ "uuid": "61cae99b-56e1-4f3e-a2b9-07fb5cf2be9e",
7
+ "name": "Important",
8
+ "count": 234
9
+ },
10
+ {
11
+ "uuid": "f80ed7b6-5e6a-49eb-94b6-4631a9677cd8",
12
+ "name": "Spam",
13
+ "count": 0
14
+ },
15
+ {
16
+ "uuid": "76e7a094-22ea-441d-91c4-283d8168e8e3",
17
+ "name": "Follow Up",
18
+ "count": 128
19
+ },
20
+ {
21
+ "uuid": "66e7a094-22ea-441d-91c4-283d8168e8e4",
22
+ "name": "Customer Service",
23
+ "count": 89
24
+ },
25
+ {
26
+ "uuid": "a3f2990b-4096-452e-a586-dc06a9434dde",
27
+ "name": "Feedback",
28
+ "count": 42
29
+ }
30
+ ]
31
+ }
@@ -3,19 +3,34 @@
3
3
  "previous": null,
4
4
  "results": [
5
5
  {
6
- "uuid": "topic-1",
7
- "name": "General Support",
8
- "created_on": "2024-01-01T00:00:00Z"
6
+ "uuid": "1b1cb507-e079-4b30-818c-1898edcbd178",
7
+ "name": "General",
8
+ "counts": {
9
+ "open": 2,
10
+ "closed": 12
11
+ },
12
+ "system": true,
13
+ "created_on": "2021-08-25T22:50:51.381947Z"
9
14
  },
10
15
  {
11
- "uuid": "topic-2",
12
- "name": "Technical Issues",
13
- "created_on": "2024-01-01T00:00:00Z"
16
+ "uuid": "bf4b568d-97b8-4d20-aed5-ad8150270af8",
17
+ "name": "Technical Support",
18
+ "counts": {
19
+ "open": 5,
20
+ "closed": 23
21
+ },
22
+ "system": false,
23
+ "created_on": "2021-08-25T22:51:15.123456Z"
14
24
  },
15
25
  {
16
- "uuid": "topic-3",
17
- "name": "Billing Questions",
18
- "created_on": "2024-01-01T00:00:00Z"
26
+ "uuid": "a3f7e9d2-1c8b-4e5f-9a6b-7d4c2e8f1a3b",
27
+ "name": "Billing",
28
+ "counts": {
29
+ "open": 1,
30
+ "closed": 8
31
+ },
32
+ "system": false,
33
+ "created_on": "2021-08-25T22:52:30.789012Z"
19
34
  }
20
35
  ]
21
36
  }