@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,23 +1,32 @@
1
1
  import { __decorate } from "tslib";
2
- import { html, css, LitElement } from 'lit';
2
+ import { html, css } from 'lit';
3
3
  import { property } from 'lit/decorators.js';
4
- import { renderMarkdown } from '../markdown';
4
+ import { RapidElement } from '../RapidElement';
5
+ import { renderMarkdownInline } from '../markdown';
5
6
  /**
6
- * A small wrapper to display labels and help text in a smartmin style.
7
- * This exists so we can display things consistently before restyling.
7
+ * FieldElement is a base class for form components that provides built-in
8
+ * field wrapper functionality, eliminating the need for manual temba-field embedding.
9
+ *
10
+ * Components extending this class only need to implement renderWidget() with their
11
+ * specific widget content, and the field wrapper (label, errors, help text) is
12
+ * automatically handled.
8
13
  */
9
- export class FormField extends LitElement {
14
+ export class FieldElement extends RapidElement {
15
+ get value() {
16
+ return this._value;
17
+ }
18
+ set value(value) {
19
+ this._value = value;
20
+ }
10
21
  constructor() {
11
- super(...arguments);
12
- this.hideLabel = false;
13
- this.widgetOnly = false;
14
- this.errors = [];
15
- this.hideErrors = false;
16
- this.helpText = '';
17
- this.helpAlways = true;
18
- this.label = '';
22
+ super();
19
23
  this.name = '';
24
+ // Use @property with custom getter/setter to handle both attribute and programmatic access
25
+ this._value = '';
26
+ this.inputRoot = this;
20
27
  this.disabled = false;
28
+ this.hideErrors = false;
29
+ this.internals = this.attachInternals();
21
30
  }
22
31
  static get styles() {
23
32
  return css `
@@ -41,19 +50,6 @@ export class FormField extends LitElement {
41
50
  line-height: normal;
42
51
  color: var(--color-text-help);
43
52
  margin-left: var(--help-text-margin-left);
44
- margin-top: -16px;
45
- opacity: 0;
46
- transition: opacity ease-in-out 100ms, margin-top ease-in-out 200ms;
47
- pointer-events: none;
48
- }
49
-
50
- .help-text.help-always {
51
- opacity: 1;
52
- margin-top: 6px;
53
- margin-left: var(--help-text-margin-left);
54
- }
55
-
56
- .field:focus-within .help-text {
57
53
  margin-top: 6px;
58
54
  opacity: 1;
59
55
  }
@@ -164,24 +160,48 @@ export class FormField extends LitElement {
164
160
  }
165
161
  updated(changedProperties) {
166
162
  super.updated(changedProperties);
163
+ if (changedProperties.has('value')) {
164
+ this.internals.setFormValue(this.value);
165
+ }
167
166
  if (changedProperties.has('errors') ||
168
167
  changedProperties.has('hideErrors')) {
169
168
  const hasErrors = !this.hideErrors && this.errors && this.errors.length > 0;
170
169
  this.classList.toggle('has-error', hasErrors);
171
170
  }
172
171
  }
173
- render() {
172
+ get form() {
173
+ return this.internals.form;
174
+ }
175
+ setValue(value) {
176
+ this.value = this.serializeValue(value);
177
+ }
178
+ getDeserializedValue() {
179
+ if (!this.value || this.value === '') {
180
+ return null;
181
+ }
182
+ return JSON.parse(this.value);
183
+ }
184
+ serializeValue(value) {
185
+ return JSON.stringify(value);
186
+ }
187
+ /**
188
+ * Renders the complete field including label, widget, errors, and help text.
189
+ * Components can override this for custom layouts, but typically should just implement renderWidget().
190
+ */
191
+ renderField() {
174
192
  const hasErrors = !this.hideErrors && this.errors && this.errors.length > 0;
175
193
  const errors = hasErrors
176
194
  ? this.errors.map((error) => {
177
195
  return html `
178
- <div class="alert-error">${renderMarkdown(error)}</div>
196
+ <div class="alert-error">${renderMarkdownInline(error)}</div>
179
197
  `;
180
198
  })
181
199
  : [];
182
200
  if (this.widgetOnly) {
183
201
  return html `
184
- <div class="${this.disabled ? 'disabled' : ''}"><slot></slot></div>
202
+ <div class="${this.disabled ? 'disabled' : ''}">
203
+ ${this.renderWidget()}
204
+ </div>
185
205
  ${errors}
186
206
  `;
187
207
  }
@@ -198,46 +218,54 @@ export class FormField extends LitElement {
198
218
  >
199
219
  `
200
220
  : null}
201
- <div class="widget">
202
- <slot></slot>
203
- ${errors}
204
- </div>
221
+ <div class="widget">${this.renderWidget()} ${errors}</div>
205
222
  ${this.helpText && this.helpText !== 'None'
206
223
  ? html `
207
- <div class="help-text ${this.helpAlways ? 'help-always' : null}">
208
- ${this.helpText}
224
+ <div class="help-text">
225
+ ${renderMarkdownInline(this.helpText)}
209
226
  </div>
210
227
  `
211
228
  : null}
212
229
  </div>
213
230
  `;
214
231
  }
232
+ /**
233
+ * Main render method that automatically provides field wrapper functionality.
234
+ * Components extending FieldElement should not override this unless they need custom field layouts.
235
+ */
236
+ render() {
237
+ return this.renderField();
238
+ }
215
239
  }
240
+ FieldElement.formAssociated = true;
216
241
  __decorate([
217
- property({ type: Boolean, attribute: 'hide_label' })
218
- ], FormField.prototype, "hideLabel", void 0);
242
+ property({ type: String })
243
+ ], FieldElement.prototype, "name", void 0);
244
+ __decorate([
245
+ property({ type: String, attribute: 'help_text' })
246
+ ], FieldElement.prototype, "helpText", void 0);
219
247
  __decorate([
220
248
  property({ type: Boolean, attribute: 'widget_only' })
221
- ], FormField.prototype, "widgetOnly", void 0);
249
+ ], FieldElement.prototype, "widgetOnly", void 0);
222
250
  __decorate([
223
- property({ type: Array, attribute: false })
224
- ], FormField.prototype, "errors", void 0);
251
+ property({ type: Array })
252
+ ], FieldElement.prototype, "errors", void 0);
225
253
  __decorate([
226
- property({ type: Boolean })
227
- ], FormField.prototype, "hideErrors", void 0);
254
+ property({ type: String })
255
+ ], FieldElement.prototype, "value", null);
228
256
  __decorate([
229
- property({ type: String, attribute: 'help_text' })
230
- ], FormField.prototype, "helpText", void 0);
257
+ property({ attribute: false })
258
+ ], FieldElement.prototype, "inputRoot", void 0);
231
259
  __decorate([
232
- property({ type: Boolean, attribute: 'help_always' })
233
- ], FormField.prototype, "helpAlways", void 0);
260
+ property({ type: Boolean })
261
+ ], FieldElement.prototype, "disabled", void 0);
234
262
  __decorate([
235
- property({ type: String })
236
- ], FormField.prototype, "label", void 0);
263
+ property({ type: Boolean })
264
+ ], FieldElement.prototype, "hideErrors", void 0);
237
265
  __decorate([
238
- property({ type: String })
239
- ], FormField.prototype, "name", void 0);
266
+ property({ type: Boolean, attribute: 'hide_label' })
267
+ ], FieldElement.prototype, "hideLabel", void 0);
240
268
  __decorate([
241
- property({ type: Boolean })
242
- ], FormField.prototype, "disabled", void 0);
243
- //# sourceMappingURL=FormField.js.map
269
+ property({ type: String })
270
+ ], FieldElement.prototype, "label", void 0);
271
+ //# sourceMappingURL=FieldElement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldElement.js","sourceRoot":"","sources":["../../../src/form/FieldElement.ts"],"names":[],"mappings":"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","sourcesContent":["import { TemplateResult, html, css } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { RapidElement } from '../RapidElement';\nimport { renderMarkdownInline } from '../markdown';\n\n/**\n * FieldElement is a base class for form components that provides built-in\n * field wrapper functionality, eliminating the need for manual temba-field embedding.\n *\n * Components extending this class only need to implement renderWidget() with their\n * specific widget content, and the field wrapper (label, errors, help text) is\n * automatically handled.\n */\nexport abstract class FieldElement extends RapidElement {\n @property({ type: String })\n name = '';\n\n @property({ type: String, attribute: 'help_text' })\n helpText: string;\n\n @property({ type: Boolean, attribute: 'widget_only' })\n widgetOnly: boolean;\n\n @property({ type: Array })\n errors: string[];\n\n // Use @property with custom getter/setter to handle both attribute and programmatic access\n private _value: any = '';\n\n @property({ type: String })\n public get value() {\n return this._value;\n }\n\n public set value(value) {\n this._value = value;\n }\n\n @property({ attribute: false })\n inputRoot: HTMLElement = this;\n\n @property({ type: Boolean })\n disabled = false;\n\n static formAssociated = true;\n\n protected internals: ElementInternals;\n\n @property({ type: Boolean })\n hideErrors = false;\n\n @property({ type: Boolean, attribute: 'hide_label' })\n hideLabel: boolean;\n\n @property({ type: String })\n label: string;\n\n constructor() {\n super();\n this.internals = this.attachInternals();\n }\n\n static get styles() {\n return css`\n :host {\n font-family: var(--font-family);\n }\n\n label {\n margin-bottom: 5px;\n margin-left: 4px;\n display: block;\n font-weight: 400;\n font-size: var(--label-size);\n letter-spacing: 0.05em;\n line-height: normal;\n color: var(--color-label, #777);\n }\n\n .help-text {\n font-size: var(--help-text-size);\n line-height: normal;\n color: var(--color-text-help);\n margin-left: var(--help-text-margin-left);\n margin-top: 6px;\n opacity: 1;\n }\n\n .alert-error {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 1000;\n background: white;\n border: 1px solid var(--color-error);\n color: var(--color-error);\n padding: 8px 12px;\n margin: 2px 0 0 0;\n border-radius: var(--curvature);\n box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),\n 0 2px 4px -1px rgba(0, 0, 0, 0.06);\n font-size: 0.85em;\n line-height: 1.2;\n opacity: 0;\n visibility: hidden;\n transform: translateY(-12px);\n transition: opacity 0.2s ease-in-out, visibility 0.2s ease-in-out,\n transform 0.2s ease-in-out;\n }\n\n .field:hover .alert-error {\n opacity: 1;\n visibility: visible;\n transform: translateY(2px);\n }\n\n /* Hide error popup when widget is focused */\n .field:focus-within .alert-error {\n opacity: 0;\n visibility: hidden;\n transform: translateY(-4px);\n }\n\n .field.has-error {\n position: relative;\n /* Set CSS custom properties that form components can use */\n --color-widget-border: var(--color-error);\n --widget-box-shadow-focused: var(\n --widget-box-shadow-focused-error,\n 0 0 0 3px rgba(255, 99, 71, 0.3)\n );\n --color-focus: var(--color-error);\n }\n\n .field.has-error .widget {\n border-radius: var(--curvature-widget);\n position: relative;\n }\n\n /* Force error styling with higher specificity */\n :host(.has-error) .field.has-error .widget .input-container,\n :host(.has-error) .field.has-error .widget .select-container,\n :host(.has-error) .field.has-error .widget .comp-container,\n :host(.has-error) .field.has-error .widget .checkbox-container,\n :host(.has-error) .field.has-error .widget .container,\n :host(.has-error) .field.has-error .widget .range-container,\n .field.has-error .widget .input-container,\n .field.has-error .widget .select-container,\n .field.has-error .widget .comp-container,\n .field.has-error .widget .checkbox-container,\n .field.has-error .widget .container,\n .field.has-error .widget .range-container {\n border-color: var(--color-error) !important;\n }\n\n /* When error field is focused, use error-colored focus ring */\n :host(.has-error) .field.has-error .widget .input-container:focus-within,\n :host(.has-error) .field.has-error .widget .select-container:focus-within,\n :host(.has-error) .field.has-error .widget .select-container.focused,\n :host(.has-error) .field.has-error .widget .comp-container:focus-within,\n :host(.has-error)\n .field.has-error\n .widget\n .checkbox-container:focus-within,\n :host(.has-error) .field.has-error .widget .container:focus-within,\n :host(.has-error) .field.has-error .widget .range-container:focus-within,\n .field.has-error .widget .input-container:focus-within,\n .field.has-error .widget .select-container:focus-within,\n .field.has-error .widget .select-container.focused,\n .field.has-error .widget .comp-container:focus-within,\n .field.has-error .widget .checkbox-container:focus-within,\n .field.has-error .widget .container:focus-within,\n .field.has-error .widget .range-container:focus-within {\n border-color: var(--color-error) !important;\n box-shadow: var(\n --widget-box-shadow-focused-error,\n 0 0 0 3px rgba(255, 99, 71, 0.3)\n ) !important;\n }\n\n .alert-error p {\n margin: 0;\n padding: 0;\n }\n\n .disabled {\n opacity: var(--disabled-opacity) !important;\n pointer-events: none !important;\n }\n `;\n }\n\n updated(changedProperties: Map<string, any>): void {\n super.updated(changedProperties);\n\n if (changedProperties.has('value')) {\n this.internals.setFormValue(this.value);\n }\n\n if (\n changedProperties.has('errors') ||\n changedProperties.has('hideErrors')\n ) {\n const hasErrors =\n !this.hideErrors && this.errors && this.errors.length > 0;\n this.classList.toggle('has-error', hasErrors);\n }\n }\n\n get form() {\n return this.internals.form;\n }\n\n public setValue(value: any) {\n this.value = this.serializeValue(value);\n }\n\n public getDeserializedValue(): any {\n if (!this.value || this.value === '') {\n return null;\n }\n return JSON.parse(this.value);\n }\n\n public serializeValue(value: any): string {\n return JSON.stringify(value);\n }\n\n /**\n * Abstract method that components must implement to render their specific widget content.\n * This replaces the need to manually embed temba-field.\n */\n protected abstract renderWidget(): TemplateResult;\n\n /**\n * Renders the complete field including label, widget, errors, and help text.\n * Components can override this for custom layouts, but typically should just implement renderWidget().\n */\n protected renderField(): TemplateResult {\n const hasErrors = !this.hideErrors && this.errors && this.errors.length > 0;\n const errors = hasErrors\n ? this.errors.map((error: string) => {\n return html`\n <div class=\"alert-error\">${renderMarkdownInline(error)}</div>\n `;\n })\n : [];\n\n if (this.widgetOnly) {\n return html`\n <div class=\"${this.disabled ? 'disabled' : ''}\">\n ${this.renderWidget()}\n </div>\n ${errors}\n `;\n }\n\n return html`\n <div\n class=\"field ${this.disabled ? 'disabled' : ''} ${hasErrors\n ? 'has-error'\n : ''}\"\n >\n ${!!this.name && !this.hideLabel && !!this.label\n ? html`\n <label class=\"control-label\" for=\"${this.name}\"\n >${this.label}</label\n >\n `\n : null}\n <div class=\"widget\">${this.renderWidget()} ${errors}</div>\n ${this.helpText && this.helpText !== 'None'\n ? html`\n <div class=\"help-text\">\n ${renderMarkdownInline(this.helpText)}\n </div>\n `\n : null}\n </div>\n `;\n }\n\n /**\n * Main render method that automatically provides field wrapper functionality.\n * Components extending FieldElement should not override this unless they need custom field layouts.\n */\n public render(): TemplateResult {\n return this.renderField();\n }\n}\n"]}
@@ -0,0 +1,306 @@
1
+ import { html } from 'lit';
2
+ /**
3
+ * FieldRenderer provides a consistent way to render field configurations
4
+ * into web components across different contexts (NodeEditor, ArrayEditor, etc.)
5
+ */
6
+ export class FieldRenderer {
7
+ /**
8
+ * Renders a field based on its configuration
9
+ * @param fieldName - The name of the field
10
+ * @param config - The field configuration
11
+ * @param value - The current value of the field
12
+ * @param context - Additional context for rendering
13
+ * @returns A TemplateResult for the rendered field
14
+ */
15
+ static renderField(fieldName, config, value, context = {}) {
16
+ /*const {
17
+ errors = [],
18
+ onChange,
19
+ showLabel = true,
20
+ flavor,
21
+ extraClasses = '',
22
+ style = ''
23
+ } = context;*/
24
+ switch (config.type) {
25
+ case 'text':
26
+ return FieldRenderer.renderTextInput(fieldName, config, value, context);
27
+ case 'textarea':
28
+ return FieldRenderer.renderTextarea(fieldName, config, value, context);
29
+ case 'select':
30
+ return FieldRenderer.renderSelect(fieldName, config, value, context);
31
+ case 'checkbox':
32
+ return FieldRenderer.renderCheckbox(fieldName, config, value, context);
33
+ case 'key-value':
34
+ return FieldRenderer.renderKeyValue(fieldName, config, value, context);
35
+ case 'array':
36
+ return FieldRenderer.renderArray(fieldName, config, value, context);
37
+ case 'message-editor':
38
+ return FieldRenderer.renderMessageEditor(fieldName, config, value, context);
39
+ default:
40
+ return html `<div>Unsupported field type: ${config.type}</div>`;
41
+ }
42
+ }
43
+ static renderTextInput(fieldName, config, value, context) {
44
+ const { errors = [], onChange, showLabel = true, extraClasses, style } = context;
45
+ // If field supports expression evaluation, use temba-completion
46
+ if (config.evaluated) {
47
+ return html `<temba-completion
48
+ name="${fieldName}"
49
+ label="${showLabel ? config.label : ''}"
50
+ ?required="${config.required}"
51
+ .errors="${errors}"
52
+ .value="${value || ''}"
53
+ placeholder="${config.placeholder || ''}"
54
+ expressions="session"
55
+ .helpText="${config.helpText || ''}"
56
+ class="${extraClasses}"
57
+ style="${style}"
58
+ @input="${onChange || (() => { })}"
59
+ ></temba-completion>`;
60
+ }
61
+ return html `<temba-textinput
62
+ name="${fieldName}"
63
+ label="${showLabel ? config.label : ''}"
64
+ ?required="${config.required}"
65
+ .errors="${errors}"
66
+ .value="${value || ''}"
67
+ placeholder="${config.placeholder || ''}"
68
+ .helpText="${config.helpText || ''}"
69
+ class="${extraClasses}"
70
+ style="${style}"
71
+ @input="${onChange || (() => { })}"
72
+ ></temba-textinput>`;
73
+ }
74
+ static renderTextarea(fieldName, config, value, context) {
75
+ const { errors = [], onChange, showLabel = true, extraClasses, style } = context;
76
+ const minHeightStyle = config.minHeight
77
+ ? `--textarea-min-height: ${config.minHeight}px;`
78
+ : '';
79
+ const combinedStyle = `${minHeightStyle}${style}`;
80
+ // If field supports expression evaluation, use temba-completion
81
+ if (config.evaluated) {
82
+ return html `<temba-completion
83
+ name="${fieldName}"
84
+ label="${showLabel ? config.label : ''}"
85
+ ?required="${config.required}"
86
+ .errors="${errors}"
87
+ .value="${value || ''}"
88
+ placeholder="${config.placeholder || ''}"
89
+ textarea
90
+ expressions="session"
91
+ .helpText="${config.helpText || ''}"
92
+ class="${extraClasses}"
93
+ style="${combinedStyle}"
94
+ @input="${onChange || (() => { })}"
95
+ ></temba-completion>`;
96
+ }
97
+ return html `<temba-textinput
98
+ name="${fieldName}"
99
+ label="${showLabel ? config.label : ''}"
100
+ ?required="${config.required}"
101
+ .errors="${errors}"
102
+ .value="${value || ''}"
103
+ placeholder="${config.placeholder || ''}"
104
+ textarea
105
+ .rows="${config.rows || 3}"
106
+ .helpText="${config.helpText || ''}"
107
+ class="${extraClasses}"
108
+ style="${combinedStyle}"
109
+ @input="${onChange || (() => { })}"
110
+ ></temba-textinput>`;
111
+ }
112
+ static renderSelect(fieldName, config, value, context) {
113
+ var _a, _b;
114
+ const { errors = [], onChange, showLabel = true, flavor, extraClasses, style } = context;
115
+ // Ensure proper value handling for multi vs single select
116
+ const normalizedValue = (() => {
117
+ if (config.multi) {
118
+ // Multi-select: ensure we have an array and convert strings to option objects
119
+ const valueArray = Array.isArray(value) ? value : value ? [value] : [];
120
+ return valueArray.map((val) => {
121
+ if (typeof val === 'string') {
122
+ // Convert string values to option objects
123
+ return { name: val, value: val };
124
+ }
125
+ return val;
126
+ });
127
+ }
128
+ else {
129
+ // Single select: use the value as-is
130
+ return value || '';
131
+ }
132
+ })();
133
+ if (typeof normalizedValue === 'string') {
134
+ return html `<temba-select
135
+ name="${fieldName}"
136
+ ?required="${config.required}"
137
+ .errors="${errors}"
138
+ value="${config.multi ? '' : normalizedValue}"
139
+ .values="${config.multi ? normalizedValue : undefined}"
140
+ ?multi="${config.multi}"
141
+ ?searchable="${config.searchable}"
142
+ ?tags="${config.tags}"
143
+ ?emails="${config.emails}"
144
+ ?clearable="${config.clearable || false}"
145
+ label="${showLabel ? config.label : ''}"
146
+ placeholder="${config.placeholder || ''}"
147
+ maxItems="${config.maxItems || 0}"
148
+ valueKey="${config.valueKey || 'value'}"
149
+ nameKey="${config.nameKey || 'name'}"
150
+ endpoint="${config.endpoint || ''}"
151
+ .helpText="${config.helpText || ''}"
152
+ flavor="${flavor || config.flavor || 'small'}"
153
+ class="${extraClasses}"
154
+ style="${style}"
155
+ .getName=${config.getName}
156
+ .createArbitraryOption=${config.createArbitraryOption}
157
+ ?allowCreate="${config.allowCreate || false}"
158
+ @change="${onChange || (() => { })}"
159
+ >
160
+ ${(_a = config.options) === null || _a === void 0 ? void 0 : _a.map((option) => {
161
+ if (typeof option === 'string') {
162
+ return html `<temba-option
163
+ name="${option}"
164
+ value="${option}"
165
+ ></temba-option>`;
166
+ }
167
+ else {
168
+ return html `<temba-option
169
+ name="${option.label || option.name}"
170
+ value="${option.value}"
171
+ ></temba-option>`;
172
+ }
173
+ })}
174
+ </temba-select>`;
175
+ }
176
+ return html `<temba-select
177
+ name="${fieldName}"
178
+ label="${showLabel ? config.label : ''}"
179
+ ?required="${config.required}"
180
+ .errors="${errors}"
181
+ .values="${normalizedValue}"
182
+ ?multi="${config.multi}"
183
+ ?searchable="${config.searchable}"
184
+ ?tags="${config.tags}"
185
+ ?emails="${config.emails}"
186
+ ?clearable="${config.clearable || false}"
187
+ placeholder="${config.placeholder || ''}"
188
+ maxItems="${config.maxItems || 0}"
189
+ valueKey="${config.valueKey || 'value'}"
190
+ nameKey="${config.nameKey || 'name'}"
191
+ endpoint="${config.endpoint || ''}"
192
+ .helpText="${config.helpText || ''}"
193
+ flavor="${flavor || config.flavor || 'small'}"
194
+ class="${extraClasses}"
195
+ style="${style}"
196
+ .getName=${config.getName}
197
+ .createArbitraryOption=${config.createArbitraryOption}
198
+ ?allowCreate="${config.allowCreate || false}"
199
+ @change="${onChange || (() => { })}"
200
+ >
201
+ ${(_b = config.options) === null || _b === void 0 ? void 0 : _b.map((option) => {
202
+ if (typeof option === 'string') {
203
+ return html `<temba-option
204
+ name="${option}"
205
+ value="${option}"
206
+ ></temba-option>`;
207
+ }
208
+ else {
209
+ return html `<temba-option
210
+ name="${option.label || option.name}"
211
+ value="${option.value}"
212
+ ></temba-option>`;
213
+ }
214
+ })}
215
+ </temba-select>`;
216
+ }
217
+ static renderCheckbox(fieldName, config, value, context) {
218
+ const { errors = [], onChange, extraClasses, style } = context;
219
+ return html `<div class="form-field">
220
+ <temba-checkbox
221
+ name="${fieldName}"
222
+ label="${config.label}"
223
+ .helpText="${config.helpText || ''}"
224
+ ?required="${config.required}"
225
+ .errors="${errors}"
226
+ ?checked="${value || false}"
227
+ size="${config.size || 1.2}"
228
+ animateChange="${config.animateChange || 'pulse'}"
229
+ class="${extraClasses}"
230
+ style="${style}"
231
+ @change="${onChange || (() => { })}"
232
+ ></temba-checkbox>
233
+ ${errors.length
234
+ ? html `<div class="field-errors">${errors.join(', ')}</div>`
235
+ : ''}
236
+ </div>`;
237
+ }
238
+ static renderKeyValue(fieldName, config, value, context) {
239
+ const { errors = [], onChange, showLabel = true, extraClasses, style } = context;
240
+ return html `<div class="form-field">
241
+ ${showLabel ? html `<label>${config.label}</label>` : ''}
242
+ <temba-key-value-editor
243
+ name="${fieldName}"
244
+ .value="${value || []}"
245
+ .sortable="${config.sortable}"
246
+ .keyPlaceholder="${config.keyPlaceholder || 'Key'}"
247
+ .valuePlaceholder="${config.valuePlaceholder || 'Value'}"
248
+ .minRows="${config.minRows || 0}"
249
+ class="${extraClasses}"
250
+ style="${style}"
251
+ @change="${onChange || (() => { })}"
252
+ ></temba-key-value-editor>
253
+ ${errors.length
254
+ ? html `<div class="field-errors">${errors.join(', ')}</div>`
255
+ : ''}
256
+ </div>`;
257
+ }
258
+ static renderArray(fieldName, config, value, context) {
259
+ const { errors = [], onChange, showLabel = true, extraClasses, style } = context;
260
+ return html `<div class="form-field">
261
+ <temba-array-editor
262
+ name="${fieldName}"
263
+ .label="${showLabel ? config.label : ''}"
264
+ .value="${value || []}"
265
+ .itemConfig="${config.itemConfig}"
266
+ .sortable="${config.sortable}"
267
+ .itemLabel="${config.itemLabel || 'Item'}"
268
+ .minItems="${config.minItems || 0}"
269
+ .maxItems="${config.maxItems || 0}"
270
+ .onItemChange="${config.onItemChange}"
271
+ .isEmptyItemFn="${config.isEmptyItem}"
272
+ class="${extraClasses}"
273
+ style="${style}"
274
+ @change="${onChange || (() => { })}"
275
+ ></temba-array-editor>
276
+ ${errors.length
277
+ ? html `<div class="field-errors">${errors.join(', ')}</div>`
278
+ : ''}
279
+ </div>`;
280
+ }
281
+ static renderMessageEditor(fieldName, config, value, context) {
282
+ const { errors = [], onChange, showLabel = true, extraClasses, style, additionalData = {} } = context;
283
+ return html `<temba-message-editor
284
+ name="${fieldName}"
285
+ label="${showLabel ? config.label : ''}"
286
+ ?required="${config.required}"
287
+ .errors="${errors}"
288
+ .value="${value || ''}"
289
+ .attachments="${additionalData.attachments || []}"
290
+ placeholder="${config.placeholder || ''}"
291
+ .helpText="${config.helpText || ''}"
292
+ ?autogrow="${config.autogrow}"
293
+ ?gsm="${config.gsm}"
294
+ ?disableCompletion="${config.disableCompletion}"
295
+ counter="${config.counter || ''}"
296
+ accept="${config.accept || ''}"
297
+ endpoint="${config.endpoint || ''}"
298
+ max-attachments="${config.maxAttachments || 3}"
299
+ minHeight="${config.minHeight || 60}"
300
+ class="${extraClasses}"
301
+ style="${style}"
302
+ @change="${onChange || (() => { })}"
303
+ ></temba-message-editor>`;
304
+ }
305
+ }
306
+ //# sourceMappingURL=FieldRenderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldRenderer.js","sourceRoot":"","sources":["../../../src/form/FieldRenderer.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, TemplateResult } from 'lit';\nimport {\n FieldConfig,\n TextFieldConfig,\n TextareaFieldConfig,\n SelectFieldConfig,\n CheckboxFieldConfig,\n MessageEditorFieldConfig,\n KeyValueFieldConfig,\n ArrayFieldConfig\n} from '../flow/types';\n\n/**\n * FieldRenderer provides a consistent way to render field configurations\n * into web components across different contexts (NodeEditor, ArrayEditor, etc.)\n */\nexport class FieldRenderer {\n /**\n * Renders a field based on its configuration\n * @param fieldName - The name of the field\n * @param config - The field configuration\n * @param value - The current value of the field\n * @param context - Additional context for rendering\n * @returns A TemplateResult for the rendered field\n */\n static renderField(\n fieldName: string,\n config: FieldConfig,\n value: any,\n context: FieldRenderContext = {}\n ): TemplateResult {\n /*const {\n errors = [],\n onChange,\n showLabel = true,\n flavor,\n extraClasses = '',\n style = ''\n } = context;*/\n switch (config.type) {\n case 'text':\n return FieldRenderer.renderTextInput(fieldName, config, value, context);\n\n case 'textarea':\n return FieldRenderer.renderTextarea(\n fieldName,\n config as TextareaFieldConfig,\n value,\n context\n );\n\n case 'select':\n return FieldRenderer.renderSelect(\n fieldName,\n config as SelectFieldConfig,\n value,\n context\n );\n\n case 'checkbox':\n return FieldRenderer.renderCheckbox(\n fieldName,\n config as CheckboxFieldConfig,\n value,\n context\n );\n\n case 'key-value':\n return FieldRenderer.renderKeyValue(\n fieldName,\n config as KeyValueFieldConfig,\n value,\n context\n );\n\n case 'array':\n return FieldRenderer.renderArray(\n fieldName,\n config as ArrayFieldConfig,\n value,\n context\n );\n\n case 'message-editor':\n return FieldRenderer.renderMessageEditor(\n fieldName,\n config as MessageEditorFieldConfig,\n value,\n context\n );\n\n default:\n return html`<div>Unsupported field type: ${(config as any).type}</div>`;\n }\n }\n\n private static renderTextInput(\n fieldName: string,\n config: TextFieldConfig,\n value: any,\n context: FieldRenderContext\n ): TemplateResult {\n const {\n errors = [],\n onChange,\n showLabel = true,\n extraClasses,\n style\n } = context;\n\n // If field supports expression evaluation, use temba-completion\n if (config.evaluated) {\n return html`<temba-completion\n name=\"${fieldName}\"\n label=\"${showLabel ? config.label : ''}\"\n ?required=\"${config.required}\"\n .errors=\"${errors}\"\n .value=\"${value || ''}\"\n placeholder=\"${config.placeholder || ''}\"\n expressions=\"session\"\n .helpText=\"${config.helpText || ''}\"\n class=\"${extraClasses}\"\n style=\"${style}\"\n @input=\"${onChange || (() => {})}\"\n ></temba-completion>`;\n }\n\n return html`<temba-textinput\n name=\"${fieldName}\"\n label=\"${showLabel ? config.label : ''}\"\n ?required=\"${config.required}\"\n .errors=\"${errors}\"\n .value=\"${value || ''}\"\n placeholder=\"${config.placeholder || ''}\"\n .helpText=\"${config.helpText || ''}\"\n class=\"${extraClasses}\"\n style=\"${style}\"\n @input=\"${onChange || (() => {})}\"\n ></temba-textinput>`;\n }\n\n private static renderTextarea(\n fieldName: string,\n config: TextareaFieldConfig,\n value: any,\n context: FieldRenderContext\n ): TemplateResult {\n const {\n errors = [],\n onChange,\n showLabel = true,\n extraClasses,\n style\n } = context;\n\n const minHeightStyle = config.minHeight\n ? `--textarea-min-height: ${config.minHeight}px;`\n : '';\n const combinedStyle = `${minHeightStyle}${style}`;\n\n // If field supports expression evaluation, use temba-completion\n if (config.evaluated) {\n return html`<temba-completion\n name=\"${fieldName}\"\n label=\"${showLabel ? config.label : ''}\"\n ?required=\"${config.required}\"\n .errors=\"${errors}\"\n .value=\"${value || ''}\"\n placeholder=\"${config.placeholder || ''}\"\n textarea\n expressions=\"session\"\n .helpText=\"${config.helpText || ''}\"\n class=\"${extraClasses}\"\n style=\"${combinedStyle}\"\n @input=\"${onChange || (() => {})}\"\n ></temba-completion>`;\n }\n\n return html`<temba-textinput\n name=\"${fieldName}\"\n label=\"${showLabel ? config.label : ''}\"\n ?required=\"${config.required}\"\n .errors=\"${errors}\"\n .value=\"${value || ''}\"\n placeholder=\"${config.placeholder || ''}\"\n textarea\n .rows=\"${config.rows || 3}\"\n .helpText=\"${config.helpText || ''}\"\n class=\"${extraClasses}\"\n style=\"${combinedStyle}\"\n @input=\"${onChange || (() => {})}\"\n ></temba-textinput>`;\n }\n\n private static renderSelect(\n fieldName: string,\n config: SelectFieldConfig,\n value: any,\n context: FieldRenderContext\n ): TemplateResult {\n const {\n errors = [],\n onChange,\n showLabel = true,\n flavor,\n extraClasses,\n style\n } = context;\n\n // Ensure proper value handling for multi vs single select\n const normalizedValue = (() => {\n if (config.multi) {\n // Multi-select: ensure we have an array and convert strings to option objects\n const valueArray = Array.isArray(value) ? value : value ? [value] : [];\n return valueArray.map((val) => {\n if (typeof val === 'string') {\n // Convert string values to option objects\n return { name: val, value: val };\n }\n return val;\n });\n } else {\n // Single select: use the value as-is\n return value || '';\n }\n })();\n\n if (typeof normalizedValue === 'string') {\n return html`<temba-select\n name=\"${fieldName}\"\n ?required=\"${config.required}\"\n .errors=\"${errors}\"\n value=\"${config.multi ? '' : normalizedValue}\"\n .values=\"${config.multi ? normalizedValue : undefined}\"\n ?multi=\"${config.multi}\"\n ?searchable=\"${config.searchable}\"\n ?tags=\"${config.tags}\"\n ?emails=\"${config.emails}\"\n ?clearable=\"${config.clearable || false}\"\n label=\"${showLabel ? config.label : ''}\"\n placeholder=\"${config.placeholder || ''}\"\n maxItems=\"${config.maxItems || 0}\"\n valueKey=\"${config.valueKey || 'value'}\"\n nameKey=\"${config.nameKey || 'name'}\"\n endpoint=\"${config.endpoint || ''}\"\n .helpText=\"${config.helpText || ''}\"\n flavor=\"${flavor || config.flavor || 'small'}\"\n class=\"${extraClasses}\"\n style=\"${style}\"\n .getName=${config.getName}\n .createArbitraryOption=${config.createArbitraryOption}\n ?allowCreate=\"${config.allowCreate || false}\"\n @change=\"${onChange || (() => {})}\"\n >\n ${config.options?.map((option: any) => {\n if (typeof option === 'string') {\n return html`<temba-option\n name=\"${option}\"\n value=\"${option}\"\n ></temba-option>`;\n } else {\n return html`<temba-option\n name=\"${option.label || option.name}\"\n value=\"${option.value}\"\n ></temba-option>`;\n }\n })}\n </temba-select>`;\n }\n\n return html`<temba-select\n name=\"${fieldName}\"\n label=\"${showLabel ? config.label : ''}\"\n ?required=\"${config.required}\"\n .errors=\"${errors}\"\n .values=\"${normalizedValue}\"\n ?multi=\"${config.multi}\"\n ?searchable=\"${config.searchable}\"\n ?tags=\"${config.tags}\"\n ?emails=\"${config.emails}\"\n ?clearable=\"${config.clearable || false}\"\n placeholder=\"${config.placeholder || ''}\"\n maxItems=\"${config.maxItems || 0}\"\n valueKey=\"${config.valueKey || 'value'}\"\n nameKey=\"${config.nameKey || 'name'}\"\n endpoint=\"${config.endpoint || ''}\"\n .helpText=\"${config.helpText || ''}\"\n flavor=\"${flavor || config.flavor || 'small'}\"\n class=\"${extraClasses}\"\n style=\"${style}\"\n .getName=${config.getName}\n .createArbitraryOption=${config.createArbitraryOption}\n ?allowCreate=\"${config.allowCreate || false}\"\n @change=\"${onChange || (() => {})}\"\n >\n ${config.options?.map((option: any) => {\n if (typeof option === 'string') {\n return html`<temba-option\n name=\"${option}\"\n value=\"${option}\"\n ></temba-option>`;\n } else {\n return html`<temba-option\n name=\"${option.label || option.name}\"\n value=\"${option.value}\"\n ></temba-option>`;\n }\n })}\n </temba-select>`;\n }\n\n private static renderCheckbox(\n fieldName: string,\n config: CheckboxFieldConfig,\n value: any,\n context: FieldRenderContext\n ): TemplateResult {\n const { errors = [], onChange, extraClasses, style } = context;\n\n return html`<div class=\"form-field\">\n <temba-checkbox\n name=\"${fieldName}\"\n label=\"${config.label}\"\n .helpText=\"${config.helpText || ''}\"\n ?required=\"${config.required}\"\n .errors=\"${errors}\"\n ?checked=\"${value || false}\"\n size=\"${config.size || 1.2}\"\n animateChange=\"${config.animateChange || 'pulse'}\"\n class=\"${extraClasses}\"\n style=\"${style}\"\n @change=\"${onChange || (() => {})}\"\n ></temba-checkbox>\n ${errors.length\n ? html`<div class=\"field-errors\">${errors.join(', ')}</div>`\n : ''}\n </div>`;\n }\n\n private static renderKeyValue(\n fieldName: string,\n config: KeyValueFieldConfig,\n value: any,\n context: FieldRenderContext\n ): TemplateResult {\n const {\n errors = [],\n onChange,\n showLabel = true,\n extraClasses,\n style\n } = context;\n\n return html`<div class=\"form-field\">\n ${showLabel ? html`<label>${config.label}</label>` : ''}\n <temba-key-value-editor\n name=\"${fieldName}\"\n .value=\"${value || []}\"\n .sortable=\"${config.sortable}\"\n .keyPlaceholder=\"${config.keyPlaceholder || 'Key'}\"\n .valuePlaceholder=\"${config.valuePlaceholder || 'Value'}\"\n .minRows=\"${config.minRows || 0}\"\n class=\"${extraClasses}\"\n style=\"${style}\"\n @change=\"${onChange || (() => {})}\"\n ></temba-key-value-editor>\n ${errors.length\n ? html`<div class=\"field-errors\">${errors.join(', ')}</div>`\n : ''}\n </div>`;\n }\n\n private static renderArray(\n fieldName: string,\n config: ArrayFieldConfig,\n value: any,\n context: FieldRenderContext\n ): TemplateResult {\n const {\n errors = [],\n onChange,\n showLabel = true,\n extraClasses,\n style\n } = context;\n\n return html`<div class=\"form-field\">\n <temba-array-editor\n name=\"${fieldName}\"\n .label=\"${showLabel ? config.label : ''}\"\n .value=\"${value || []}\"\n .itemConfig=\"${config.itemConfig}\"\n .sortable=\"${config.sortable}\"\n .itemLabel=\"${config.itemLabel || 'Item'}\"\n .minItems=\"${config.minItems || 0}\"\n .maxItems=\"${config.maxItems || 0}\"\n .onItemChange=\"${config.onItemChange}\"\n .isEmptyItemFn=\"${config.isEmptyItem}\"\n class=\"${extraClasses}\"\n style=\"${style}\"\n @change=\"${onChange || (() => {})}\"\n ></temba-array-editor>\n ${errors.length\n ? html`<div class=\"field-errors\">${errors.join(', ')}</div>`\n : ''}\n </div>`;\n }\n\n private static renderMessageEditor(\n fieldName: string,\n config: MessageEditorFieldConfig,\n value: any,\n context: FieldRenderContext\n ): TemplateResult {\n const {\n errors = [],\n onChange,\n showLabel = true,\n extraClasses,\n style,\n additionalData = {}\n } = context;\n\n return html`<temba-message-editor\n name=\"${fieldName}\"\n label=\"${showLabel ? config.label : ''}\"\n ?required=\"${config.required}\"\n .errors=\"${errors}\"\n .value=\"${value || ''}\"\n .attachments=\"${additionalData.attachments || []}\"\n placeholder=\"${config.placeholder || ''}\"\n .helpText=\"${config.helpText || ''}\"\n ?autogrow=\"${config.autogrow}\"\n ?gsm=\"${config.gsm}\"\n ?disableCompletion=\"${config.disableCompletion}\"\n counter=\"${config.counter || ''}\"\n accept=\"${config.accept || ''}\"\n endpoint=\"${config.endpoint || ''}\"\n max-attachments=\"${config.maxAttachments || 3}\"\n minHeight=\"${config.minHeight || 60}\"\n class=\"${extraClasses}\"\n style=\"${style}\"\n @change=\"${onChange || (() => {})}\"\n ></temba-message-editor>`;\n }\n}\n\n/**\n * Context object for field rendering that provides additional options\n */\nexport interface FieldRenderContext {\n /** Array of error messages for the field */\n errors?: string[];\n /** Change event handler */\n onChange?: (event: Event) => void;\n /** Whether to show the field label */\n showLabel?: boolean;\n /** Flavor for components that support it (like temba-select) */\n flavor?: string;\n /** Additional CSS classes to apply */\n extraClasses?: string;\n /** Additional CSS styles to apply */\n style?: string;\n /** Additional data needed for specific field types */\n additionalData?: Record<string, any>;\n}\n"]}