@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.
- package/CHANGELOG.md +37 -3
- package/demo/data/flows/sample-flow.json +186 -96
- package/demo/test-colorpicker.html +30 -0
- package/dist/temba-components.js +1126 -1111
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/events.js.map +1 -1
- package/out-tsc/src/excellent/helpers.js +2 -2
- package/out-tsc/src/excellent/helpers.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +25 -7
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +11 -1
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +133 -290
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/actions/add_input_labels.js +40 -0
- package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
- package/out-tsc/src/flow/actions/call_llm.js +56 -3
- package/out-tsc/src/flow/actions/call_llm.js.map +1 -1
- package/out-tsc/src/flow/actions/call_webhook.js +1 -1
- package/out-tsc/src/flow/actions/call_webhook.js.map +1 -1
- package/out-tsc/src/flow/actions/open_ticket.js +65 -3
- package/out-tsc/src/flow/actions/open_ticket.js.map +1 -1
- package/out-tsc/src/flow/actions/set_run_result.js +75 -0
- package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
- package/out-tsc/src/flow/config.js +4 -0
- package/out-tsc/src/flow/config.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +227 -0
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_ticket.js +18 -0
- package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_response.js +27 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/flow/types.js +0 -65
- package/out-tsc/src/flow/types.js.map +1 -1
- package/out-tsc/src/form/ArrayEditor.js +63 -117
- package/out-tsc/src/form/ArrayEditor.js.map +1 -1
- package/out-tsc/src/form/BaseListEditor.js +4 -3
- package/out-tsc/src/form/BaseListEditor.js.map +1 -1
- package/out-tsc/src/form/Checkbox.js +77 -24
- package/out-tsc/src/form/Checkbox.js.map +1 -1
- package/out-tsc/src/form/ColorPicker.js +28 -40
- package/out-tsc/src/form/ColorPicker.js.map +1 -1
- package/out-tsc/src/form/Completion.js +44 -53
- package/out-tsc/src/form/Completion.js.map +1 -1
- package/out-tsc/src/form/Compose.js +7 -8
- package/out-tsc/src/form/Compose.js.map +1 -1
- package/out-tsc/src/form/ContactSearch.js +3 -4
- package/out-tsc/src/form/ContactSearch.js.map +1 -1
- package/out-tsc/src/form/DatePicker.js +29 -36
- package/out-tsc/src/form/DatePicker.js.map +1 -1
- package/out-tsc/src/form/{FormField.js → FieldElement.js} +81 -53
- package/out-tsc/src/form/FieldElement.js.map +1 -0
- package/out-tsc/src/form/FieldRenderer.js +306 -0
- package/out-tsc/src/form/FieldRenderer.js.map +1 -0
- package/out-tsc/src/form/ImagePicker.js +122 -126
- package/out-tsc/src/form/ImagePicker.js.map +1 -1
- package/out-tsc/src/form/KeyValueEditor.js +41 -37
- package/out-tsc/src/form/KeyValueEditor.js.map +1 -1
- package/out-tsc/src/form/MessageEditor.js +55 -63
- package/out-tsc/src/form/MessageEditor.js.map +1 -1
- package/out-tsc/src/form/TembaSlider.js +3 -3
- package/out-tsc/src/form/TembaSlider.js.map +1 -1
- package/out-tsc/src/form/TemplateEditor.js +3 -3
- package/out-tsc/src/form/TemplateEditor.js.map +1 -1
- package/out-tsc/src/form/TextInput.js +23 -27
- package/out-tsc/src/form/TextInput.js.map +1 -1
- package/out-tsc/src/form/select/Select.js +57 -35
- package/out-tsc/src/form/select/Select.js.map +1 -1
- package/out-tsc/src/form/select/UserSelect.js +8 -9
- package/out-tsc/src/form/select/UserSelect.js.map +1 -1
- package/out-tsc/src/form/select/WorkspaceSelect.js +7 -8
- package/out-tsc/src/form/select/WorkspaceSelect.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +62 -44
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/live/ContactFieldEditor.js.map +1 -1
- package/out-tsc/src/markdown.js +13 -11
- package/out-tsc/src/markdown.js.map +1 -1
- package/out-tsc/temba-modules.js +3 -2
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/ActionHelper.js +2 -0
- package/out-tsc/test/ActionHelper.js.map +1 -1
- package/out-tsc/test/NodeHelper.js +148 -0
- package/out-tsc/test/NodeHelper.js.map +1 -0
- package/out-tsc/test/actions/call_llm.test.js +103 -0
- package/out-tsc/test/actions/call_llm.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_llm_categorize.test.js +532 -0
- package/out-tsc/test/nodes/split_by_llm_categorize.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_random.test.js +150 -0
- package/out-tsc/test/nodes/split_by_random.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_digits.test.js +150 -0
- package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_response.test.js +171 -0
- package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -0
- package/out-tsc/test/temba-add-input-labels.test.js +70 -0
- package/out-tsc/test/temba-add-input-labels.test.js.map +1 -0
- package/out-tsc/test/temba-checkbox.test.js +16 -0
- package/out-tsc/test/temba-checkbox.test.js.map +1 -1
- package/out-tsc/test/temba-field-renderer.test.js +296 -0
- package/out-tsc/test/temba-field-renderer.test.js.map +1 -0
- package/out-tsc/test/temba-integration-markdown.test.js +2 -4
- package/out-tsc/test/temba-integration-markdown.test.js.map +1 -1
- package/out-tsc/test/temba-markdown.test.js +1 -1
- package/out-tsc/test/temba-markdown.test.js.map +1 -1
- package/out-tsc/test/temba-node-editor.test.js +400 -0
- package/out-tsc/test/temba-node-editor.test.js.map +1 -1
- package/out-tsc/test/temba-select.test.js +6 -3
- package/out-tsc/test/temba-select.test.js.map +1 -1
- package/out-tsc/test/temba-slider.test.js +0 -1
- package/out-tsc/test/temba-slider.test.js.map +1 -1
- package/out-tsc/test/temba-webchat.test.js +1 -1
- package/out-tsc/test/temba-webchat.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/information-extraction.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/sentiment-analysis.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/summarization.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/translation-task.png +0 -0
- package/screenshots/truth/actions/call_llm/render/information-extraction.png +0 -0
- package/screenshots/truth/actions/call_llm/render/sentiment-analysis.png +0 -0
- package/screenshots/truth/actions/call_llm/render/summarization.png +0 -0
- package/screenshots/truth/actions/call_llm/render/translation-task.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
- package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
- package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
- package/screenshots/truth/checkbox/checkbox-label-background-hover.png +0 -0
- package/screenshots/truth/checkbox/checkbox-no-label-no-background-hover.png +0 -0
- package/screenshots/truth/checkbox/checkbox-with-help-text.png +0 -0
- package/screenshots/truth/checkbox/checked.png +0 -0
- package/screenshots/truth/checkbox/default.png +0 -0
- package/screenshots/truth/colorpicker/default.png +0 -0
- package/screenshots/truth/colorpicker/focused.png +0 -0
- package/screenshots/truth/colorpicker/initialized.png +0 -0
- package/screenshots/truth/colorpicker/selected.png +0 -0
- package/screenshots/truth/editor/router.png +0 -0
- package/screenshots/truth/editor/send_msg.png +0 -0
- package/screenshots/truth/editor/set_contact_language.png +0 -0
- package/screenshots/truth/editor/set_contact_name.png +0 -0
- package/screenshots/truth/editor/set_run_result.png +0 -0
- package/screenshots/truth/editor/wait.png +0 -0
- package/screenshots/truth/field-renderer/checkbox-checked.png +0 -0
- package/screenshots/truth/field-renderer/checkbox-unchecked.png +0 -0
- package/screenshots/truth/field-renderer/checkbox-with-errors.png +0 -0
- package/screenshots/truth/field-renderer/context-comparison.png +0 -0
- package/screenshots/truth/field-renderer/key-value-with-label.png +0 -0
- package/screenshots/truth/field-renderer/message-editor-with-label.png +0 -0
- package/screenshots/truth/field-renderer/select-multi.png +0 -0
- package/screenshots/truth/field-renderer/select-no-label.png +0 -0
- package/screenshots/truth/field-renderer/select-with-label.png +0 -0
- package/screenshots/truth/field-renderer/text-evaluated.png +0 -0
- package/screenshots/truth/field-renderer/text-no-label.png +0 -0
- package/screenshots/truth/field-renderer/text-with-errors.png +0 -0
- package/screenshots/truth/field-renderer/text-with-label.png +0 -0
- package/screenshots/truth/field-renderer/textarea-evaluated.png +0 -0
- package/screenshots/truth/field-renderer/textarea-with-label.png +0 -0
- package/screenshots/truth/integration/checkbox-markdown-errors.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
- package/screenshots/truth/omnibox/selected.png +0 -0
- package/screenshots/truth/run-list/basic.png +0 -0
- package/screenshots/truth/select/functions.png +0 -0
- package/screenshots/truth/select/multi-with-endpoint.png +0 -0
- package/screenshots/truth/select/search-enabled.png +0 -0
- package/src/events.ts +12 -6
- package/src/excellent/helpers.ts +2 -2
- package/src/flow/CanvasNode.ts +22 -1
- package/src/flow/Editor.ts +12 -1
- package/src/flow/NodeEditor.ts +186 -374
- package/src/flow/actions/add_input_labels.ts +45 -0
- package/src/flow/actions/call_llm.ts +57 -3
- package/src/flow/actions/call_webhook.ts +1 -1
- package/src/flow/actions/open_ticket.ts +74 -3
- package/src/flow/actions/set_run_result.ts +83 -0
- package/src/flow/config.ts +4 -0
- package/src/flow/nodes/split_by_llm_categorize.ts +277 -0
- package/src/flow/nodes/split_by_ticket.ts +19 -0
- package/src/flow/nodes/wait_for_response.ts +28 -1
- package/src/flow/types.ts +26 -127
- package/src/form/ArrayEditor.ts +79 -139
- package/src/form/BaseListEditor.ts +4 -4
- package/src/form/Checkbox.ts +81 -24
- package/src/form/ColorPicker.ts +31 -43
- package/src/form/Completion.ts +49 -56
- package/src/form/Compose.ts +8 -8
- package/src/form/ContactSearch.ts +3 -4
- package/src/form/DatePicker.ts +32 -38
- package/src/form/{FormField.ts → FieldElement.ts} +108 -55
- package/src/form/FieldRenderer.ts +466 -0
- package/src/form/ImagePicker.ts +107 -110
- package/src/form/KeyValueEditor.ts +43 -39
- package/src/form/MessageEditor.ts +61 -67
- package/src/form/TembaSlider.ts +3 -3
- package/src/form/TemplateEditor.ts +3 -3
- package/src/form/TextInput.ts +26 -29
- package/src/form/select/Select.ts +63 -37
- package/src/form/select/UserSelect.ts +10 -11
- package/src/form/select/WorkspaceSelect.ts +9 -10
- package/src/live/ContactChat.ts +62 -47
- package/src/live/ContactFieldEditor.ts +2 -2
- package/src/markdown.ts +19 -11
- package/src/store/flow-definition.d.ts +5 -2
- package/static/api/labels.json +31 -0
- package/static/api/topics.json +24 -9
- package/static/api/users.json +35 -16
- package/static/css/temba-components.css +3 -3
- package/stress-test.js +18 -13
- package/temba-modules.ts +3 -2
- package/test/ActionHelper.ts +2 -0
- package/test/NodeHelper.ts +184 -0
- package/test/actions/call_llm.test.ts +137 -0
- package/test/nodes/README.md +78 -0
- package/test/nodes/split_by_llm_categorize.test.ts +698 -0
- package/test/nodes/split_by_random.test.ts +177 -0
- package/test/nodes/wait_for_digits.test.ts +176 -0
- package/test/nodes/wait_for_response.test.ts +206 -0
- package/test/temba-add-input-labels.test.ts +87 -0
- package/test/temba-checkbox.test.ts +26 -0
- package/test/temba-field-renderer.test.ts +482 -0
- package/test/temba-integration-markdown.test.ts +2 -4
- package/test/temba-markdown.test.ts +1 -1
- package/test/temba-node-editor.test.ts +496 -0
- package/test/temba-select.test.ts +6 -6
- package/test/temba-slider.test.ts +0 -1
- package/test/temba-webchat.test.ts +1 -1
- package/test-assets/contacts/history.json +7 -20
- package/test-assets/select/llms.json +18 -0
- package/web-dev-mock.mjs +96 -6
- package/web-dev-server.config.mjs +29 -7
- package/out-tsc/src/form/FormElement.js +0 -67
- package/out-tsc/src/form/FormElement.js.map +0 -1
- package/out-tsc/src/form/FormField.js.map +0 -1
- package/out-tsc/test/temba-formfield.test.js +0 -94
- package/out-tsc/test/temba-formfield.test.js.map +0 -1
- package/src/form/FormElement.ts +0 -69
- package/test/temba-flow-editor.test.ts.backup +0 -563
- package/test/temba-formfield.test.ts +0 -121
- package/test/temba-utils-index.test.ts.backup +0 -1737
|
@@ -1,23 +1,32 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
|
-
import { html, css
|
|
2
|
+
import { html, css } from 'lit';
|
|
3
3
|
import { property } from 'lit/decorators.js';
|
|
4
|
-
import {
|
|
4
|
+
import { RapidElement } from '../RapidElement';
|
|
5
|
+
import { renderMarkdownInline } from '../markdown';
|
|
5
6
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|
|
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(
|
|
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
|
-
|
|
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">${
|
|
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' : ''}"
|
|
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
|
|
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:
|
|
218
|
-
],
|
|
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
|
-
],
|
|
249
|
+
], FieldElement.prototype, "widgetOnly", void 0);
|
|
222
250
|
__decorate([
|
|
223
|
-
property({ type: Array
|
|
224
|
-
],
|
|
251
|
+
property({ type: Array })
|
|
252
|
+
], FieldElement.prototype, "errors", void 0);
|
|
225
253
|
__decorate([
|
|
226
|
-
property({ type:
|
|
227
|
-
],
|
|
254
|
+
property({ type: String })
|
|
255
|
+
], FieldElement.prototype, "value", null);
|
|
228
256
|
__decorate([
|
|
229
|
-
property({
|
|
230
|
-
],
|
|
257
|
+
property({ attribute: false })
|
|
258
|
+
], FieldElement.prototype, "inputRoot", void 0);
|
|
231
259
|
__decorate([
|
|
232
|
-
property({ type: Boolean
|
|
233
|
-
],
|
|
260
|
+
property({ type: Boolean })
|
|
261
|
+
], FieldElement.prototype, "disabled", void 0);
|
|
234
262
|
__decorate([
|
|
235
|
-
property({ type:
|
|
236
|
-
],
|
|
263
|
+
property({ type: Boolean })
|
|
264
|
+
], FieldElement.prototype, "hideErrors", void 0);
|
|
237
265
|
__decorate([
|
|
238
|
-
property({ type:
|
|
239
|
-
],
|
|
266
|
+
property({ type: Boolean, attribute: 'hide_label' })
|
|
267
|
+
], FieldElement.prototype, "hideLabel", void 0);
|
|
240
268
|
__decorate([
|
|
241
|
-
property({ type:
|
|
242
|
-
],
|
|
243
|
-
//# sourceMappingURL=
|
|
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"]}
|