@nyaruka/temba-components 0.129.7 → 0.129.9
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/.devcontainer/Dockerfile +11 -4
- package/.devcontainer/devcontainer.json +3 -2
- package/.github/workflows/build.yml +4 -14
- package/CHANGELOG.md +29 -0
- package/demo/components/flow/example.html +1 -1
- package/demo/components/message-editor/example.html +125 -0
- package/demo/components/textinput/completion.html +1 -0
- package/demo/data/flows/food-order.json +12 -21
- package/demo/data/flows/sample-flow.json +210 -104
- package/dist/temba-components.js +715 -364
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/display/Thumbnail.js +2 -1
- package/out-tsc/src/display/Thumbnail.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 +342 -276
- 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 +26 -17
- 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/send_msg.js +147 -6
- package/out-tsc/src/flow/actions/send_msg.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 +87 -57
- package/out-tsc/src/form/ArrayEditor.js.map +1 -1
- package/out-tsc/src/form/BaseListEditor.js +19 -4
- package/out-tsc/src/form/BaseListEditor.js.map +1 -1
- package/out-tsc/src/form/FieldRenderer.js +305 -0
- package/out-tsc/src/form/FieldRenderer.js.map +1 -0
- package/out-tsc/src/form/FormField.js +4 -4
- package/out-tsc/src/form/FormField.js.map +1 -1
- package/out-tsc/src/form/KeyValueEditor.js +1 -1
- package/out-tsc/src/form/KeyValueEditor.js.map +1 -1
- package/out-tsc/src/form/MediaPicker.js +13 -1
- package/out-tsc/src/form/MediaPicker.js.map +1 -1
- package/out-tsc/src/form/MessageEditor.js +422 -0
- package/out-tsc/src/form/MessageEditor.js.map +1 -0
- package/out-tsc/src/form/TextInput.js +13 -6
- package/out-tsc/src/form/TextInput.js.map +1 -1
- package/out-tsc/src/form/select/Select.js +52 -24
- package/out-tsc/src/form/select/Select.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +66 -15
- package/out-tsc/src/live/ContactChat.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 +2 -0
- 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-field-config.test.js +4 -2
- package/out-tsc/test/temba-field-config.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-markdown.test.js +1 -1
- package/out-tsc/test/temba-markdown.test.js.map +1 -1
- package/out-tsc/test/temba-message-editor.test.js +194 -0
- package/out-tsc/test/temba-message-editor.test.js.map +1 -0
- package/out-tsc/test/temba-node-editor.test.js +471 -0
- package/out-tsc/test/temba-node-editor.test.js.map +1 -1
- package/out-tsc/test/temba-select.test.js +7 -4
- package/out-tsc/test/temba-select.test.js.map +1 -1
- package/out-tsc/test/temba-textinput.test.js +16 -0
- package/out-tsc/test/temba-textinput.test.js.map +1 -1
- package/out-tsc/test/temba-webchat.test.js +5 -1
- package/out-tsc/test/temba-webchat.test.js.map +1 -1
- package/out-tsc/test/utils.test.js +2 -8
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +7 -4
- 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/add_contact_groups/editor/multiple-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/single-group.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/empty-body.png +0 -0
- package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
- package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
- package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
- package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
- package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
- package/screenshots/truth/actions/send_email/editor/with-expressions.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/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/formfield/markdown-errors.png +0 -0
- package/screenshots/truth/formfield/no-errors.png +0 -0
- package/screenshots/truth/formfield/plain-text-errors.png +0 -0
- package/screenshots/truth/message-editor/autogrow-initial-content.png +0 -0
- package/screenshots/truth/message-editor/default.png +0 -0
- package/screenshots/truth/message-editor/drag-highlight.png +0 -0
- package/screenshots/truth/message-editor/filtered-attachments.png +0 -0
- package/screenshots/truth/message-editor/with-completion.png +0 -0
- package/screenshots/truth/message-editor/with-properties.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/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/screenshots/truth/textinput/autogrow-initial.png +0 -0
- package/screenshots/truth/textinput/input-form.png +0 -0
- package/src/display/Thumbnail.ts +2 -1
- package/src/events.ts +13 -1
- 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 +412 -354
- 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 +28 -18
- package/src/flow/actions/open_ticket.ts +74 -3
- package/src/flow/actions/send_msg.ts +170 -6
- 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 +46 -128
- package/src/form/ArrayEditor.ts +96 -66
- package/src/form/BaseListEditor.ts +22 -6
- package/src/form/FieldRenderer.ts +465 -0
- package/src/form/FormField.ts +4 -4
- package/src/form/KeyValueEditor.ts +1 -1
- package/src/form/MediaPicker.ts +13 -1
- package/src/form/MessageEditor.ts +449 -0
- package/src/form/TextInput.ts +16 -8
- package/src/form/select/Select.ts +55 -24
- package/src/live/ContactChat.ts +69 -19
- 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 +5 -3
- package/static/mr/docs/en-us/editor.json +2588 -0
- package/stress-test.js +143 -0
- package/temba-modules.ts +2 -0
- 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-field-config.test.ts +4 -2
- package/test/temba-field-renderer.test.ts +482 -0
- package/test/temba-markdown.test.ts +1 -1
- package/test/temba-message-editor.test.ts +300 -0
- package/test/temba-node-editor.test.ts +590 -0
- package/test/temba-select.test.ts +7 -7
- package/test/temba-textinput.test.ts +26 -0
- package/test/temba-webchat.test.ts +6 -1
- package/test/utils.test.ts +2 -13
- package/test-assets/contacts/history.json +19 -0
- package/test-assets/select/llms.json +18 -0
- package/test-assets/style.css +2 -0
- package/web-dev-mock.mjs +523 -0
- package/web-dev-server.config.mjs +74 -6
- package/web-test-runner.config.mjs +9 -4
- package/test/temba-flow-editor.test.ts.backup +0 -563
- package/test/temba-utils-index.test.ts.backup +0 -1737
package/src/form/ArrayEditor.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { html, css, TemplateResult } from 'lit';
|
|
2
2
|
import { customElement, property } from 'lit/decorators.js';
|
|
3
|
-
import { FieldConfig } from '../flow/types';
|
|
3
|
+
import { FieldConfig, SelectFieldConfig } from '../flow/types';
|
|
4
4
|
import { BaseListEditor, ListItem } from './BaseListEditor';
|
|
5
|
+
import { FieldRenderer } from './FieldRenderer';
|
|
5
6
|
|
|
6
7
|
@customElement('temba-array-editor')
|
|
7
8
|
export class TembaArrayEditor extends BaseListEditor<ListItem> {
|
|
@@ -19,6 +20,12 @@ export class TembaArrayEditor extends BaseListEditor<ListItem> {
|
|
|
19
20
|
allItems: any[]
|
|
20
21
|
) => any[];
|
|
21
22
|
|
|
23
|
+
@property({ type: Function })
|
|
24
|
+
isEmptyItemFn?: (item: any) => boolean;
|
|
25
|
+
|
|
26
|
+
@property({ type: Boolean })
|
|
27
|
+
maintainEmptyItem = true; // Enable by default for better UX
|
|
28
|
+
|
|
22
29
|
constructor() {
|
|
23
30
|
super();
|
|
24
31
|
this._items = [];
|
|
@@ -37,11 +44,36 @@ export class TembaArrayEditor extends BaseListEditor<ListItem> {
|
|
|
37
44
|
|
|
38
45
|
// Implement abstract methods
|
|
39
46
|
isEmptyItem(item: ListItem): boolean {
|
|
40
|
-
|
|
47
|
+
// Use configurable function if provided
|
|
48
|
+
if (this.isEmptyItemFn) {
|
|
49
|
+
return this.isEmptyItemFn(item);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Default behavior: check if all values are empty
|
|
53
|
+
const values = Object.values(item);
|
|
54
|
+
if (values.length === 0) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return values.every(
|
|
41
59
|
(value) => value === undefined || value === null || value === ''
|
|
42
60
|
);
|
|
43
61
|
}
|
|
44
62
|
|
|
63
|
+
// Override cleanItems to be more permissive for form data
|
|
64
|
+
protected cleanItems(items: ListItem[]): any {
|
|
65
|
+
// For runtime attachments, keep items that have at least one non-empty field
|
|
66
|
+
return items.filter((item) => {
|
|
67
|
+
const values = Object.values(item);
|
|
68
|
+
return (
|
|
69
|
+
values.length > 0 &&
|
|
70
|
+
values.some(
|
|
71
|
+
(value) => value !== undefined && value !== null && value !== ''
|
|
72
|
+
)
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
45
77
|
createEmptyItem(): ListItem {
|
|
46
78
|
return {};
|
|
47
79
|
}
|
|
@@ -83,6 +115,14 @@ export class TembaArrayEditor extends BaseListEditor<ListItem> {
|
|
|
83
115
|
return config.computeValue(item, currentValue);
|
|
84
116
|
}
|
|
85
117
|
|
|
118
|
+
// For select fields, ensure we return the right type
|
|
119
|
+
if (config.type === 'select') {
|
|
120
|
+
const selectConfig = config as SelectFieldConfig;
|
|
121
|
+
if (currentValue === undefined || currentValue === null) {
|
|
122
|
+
return selectConfig.multi ? [] : '';
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
86
126
|
return currentValue;
|
|
87
127
|
}
|
|
88
128
|
|
|
@@ -93,36 +133,42 @@ export class TembaArrayEditor extends BaseListEditor<ListItem> {
|
|
|
93
133
|
): TemplateResult {
|
|
94
134
|
const computedValue = this.computeFieldValue(itemIndex, fieldName, config);
|
|
95
135
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
136
|
+
// Use FieldRenderer for consistent field rendering
|
|
137
|
+
return FieldRenderer.renderField(fieldName, config, computedValue, {
|
|
138
|
+
showLabel: false, // ArrayEditor doesn't show labels for individual fields
|
|
139
|
+
flavor: 'small', // ArrayEditor uses small flavor
|
|
140
|
+
extraClasses: 'form-control',
|
|
141
|
+
onChange: (e: Event) => {
|
|
142
|
+
let value: any;
|
|
143
|
+
const target = e.target as any;
|
|
144
|
+
|
|
145
|
+
// Handle different field types and their change events
|
|
146
|
+
if (config.type === 'select') {
|
|
147
|
+
// For temba-select, extract the correct value
|
|
148
|
+
if (target.tagName === 'TEMBA-SELECT') {
|
|
149
|
+
if (target.multi || target.emails || target.tags) {
|
|
150
|
+
value = target.values || [];
|
|
151
|
+
} else {
|
|
152
|
+
// Single select: extract value from first selected option
|
|
153
|
+
const values = target.values || [];
|
|
154
|
+
value =
|
|
155
|
+
values.length > 0 && values[0]
|
|
156
|
+
? values[0].value !== undefined
|
|
157
|
+
? values[0].value
|
|
158
|
+
: values[0]
|
|
159
|
+
: '';
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
value = target.value;
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
// For other field types, use the target value directly
|
|
166
|
+
value = target.value;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
this.handleFieldChange(itemIndex, fieldName, value);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
126
172
|
}
|
|
127
173
|
|
|
128
174
|
renderItem(item: ListItem, index: number): TemplateResult {
|
|
@@ -130,29 +176,25 @@ export class TembaArrayEditor extends BaseListEditor<ListItem> {
|
|
|
130
176
|
|
|
131
177
|
return html`
|
|
132
178
|
<div class="array-item">
|
|
133
|
-
<div class="item-
|
|
134
|
-
|
|
179
|
+
<div class="item-fields">
|
|
180
|
+
${Object.entries(this.itemConfig).map(
|
|
181
|
+
([fieldName, config]) => html`
|
|
182
|
+
<div class="field">
|
|
183
|
+
${this.renderField(index, fieldName, config)}
|
|
184
|
+
</div>
|
|
185
|
+
`
|
|
186
|
+
)}
|
|
135
187
|
${canRemove
|
|
136
188
|
? html`
|
|
137
189
|
<button
|
|
138
190
|
@click=${() => this.removeItem(index)}
|
|
139
191
|
class="remove-btn"
|
|
140
192
|
>
|
|
141
|
-
|
|
193
|
+
<temba-icon name="x"></temba-icon>
|
|
142
194
|
</button>
|
|
143
195
|
`
|
|
144
196
|
: ''}
|
|
145
197
|
</div>
|
|
146
|
-
<div class="item-fields">
|
|
147
|
-
${Object.entries(this.itemConfig).map(
|
|
148
|
-
([fieldName, config]) => html`
|
|
149
|
-
<div class="field">
|
|
150
|
-
<label>${config.label}${config.required ? ' *' : ''}</label>
|
|
151
|
-
${this.renderField(index, fieldName, config)}
|
|
152
|
-
</div>
|
|
153
|
-
`
|
|
154
|
-
)}
|
|
155
|
-
</div>
|
|
156
198
|
</div>
|
|
157
199
|
`;
|
|
158
200
|
}
|
|
@@ -171,27 +213,15 @@ export class TembaArrayEditor extends BaseListEditor<ListItem> {
|
|
|
171
213
|
|
|
172
214
|
static styles = css`
|
|
173
215
|
.array-editor {
|
|
174
|
-
border: 1px solid #e0e0e0;
|
|
175
|
-
border-radius: 6px;
|
|
176
|
-
padding: 16px;
|
|
177
|
-
background: #fafafa;
|
|
178
216
|
}
|
|
179
217
|
|
|
180
218
|
.array-item {
|
|
181
|
-
border: 1px solid #d0d0d0;
|
|
182
|
-
border-radius: 4px;
|
|
183
|
-
padding: 16px;
|
|
184
|
-
margin-bottom: 12px;
|
|
185
|
-
background: white;
|
|
186
219
|
}
|
|
187
220
|
|
|
188
221
|
.item-header {
|
|
189
222
|
display: flex;
|
|
190
223
|
justify-content: space-between;
|
|
191
224
|
align-items: center;
|
|
192
|
-
margin-bottom: 12px;
|
|
193
|
-
padding-bottom: 8px;
|
|
194
|
-
border-bottom: 1px solid #eee;
|
|
195
225
|
}
|
|
196
226
|
|
|
197
227
|
.item-title {
|
|
@@ -200,8 +230,13 @@ export class TembaArrayEditor extends BaseListEditor<ListItem> {
|
|
|
200
230
|
}
|
|
201
231
|
|
|
202
232
|
.item-fields {
|
|
203
|
-
display:
|
|
233
|
+
display: flex;
|
|
204
234
|
gap: 12px;
|
|
235
|
+
align-items: center;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.field {
|
|
239
|
+
flex: 1;
|
|
205
240
|
}
|
|
206
241
|
|
|
207
242
|
.field label {
|
|
@@ -214,7 +249,7 @@ export class TembaArrayEditor extends BaseListEditor<ListItem> {
|
|
|
214
249
|
|
|
215
250
|
.add-btn,
|
|
216
251
|
.remove-btn {
|
|
217
|
-
padding: 8px
|
|
252
|
+
padding: 8px;
|
|
218
253
|
border: 1px solid #ccc;
|
|
219
254
|
border-radius: 4px;
|
|
220
255
|
background: white;
|
|
@@ -228,13 +263,8 @@ export class TembaArrayEditor extends BaseListEditor<ListItem> {
|
|
|
228
263
|
}
|
|
229
264
|
|
|
230
265
|
.remove-btn {
|
|
231
|
-
background: #
|
|
232
|
-
|
|
233
|
-
color: #dc2626;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
.remove-btn:hover {
|
|
237
|
-
background: #fef2f2;
|
|
266
|
+
background: #fefefe;
|
|
267
|
+
color: #999;
|
|
238
268
|
}
|
|
239
269
|
`;
|
|
240
270
|
}
|
|
@@ -52,10 +52,12 @@ export abstract class BaseListEditor<
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
protected shouldShowAddButton(): boolean {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
// Never show add button when maintaining empty items (auto-add behavior)
|
|
56
|
+
if (this.maintainEmptyItem) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return !this.maxItems || this._items.length < this.maxItems;
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
render(): TemplateResult {
|
|
@@ -65,7 +67,7 @@ export abstract class BaseListEditor<
|
|
|
65
67
|
<div class=${this.getContainerClass()}>
|
|
66
68
|
<div
|
|
67
69
|
class="list-items"
|
|
68
|
-
style="
|
|
70
|
+
style="display: grid; grid-template-columns: 1fr; gap: 8px;"
|
|
69
71
|
>
|
|
70
72
|
${items.map((item, index) => this.renderItem(item, index))}
|
|
71
73
|
</div>
|
|
@@ -89,7 +91,8 @@ export abstract class BaseListEditor<
|
|
|
89
91
|
|
|
90
92
|
if (this.maintainEmptyItem) {
|
|
91
93
|
const hasEmptyItem = items.some((item) => this.isEmptyItem(item));
|
|
92
|
-
if
|
|
94
|
+
// Only add empty item if we haven't reached maxItems and don't already have an empty item
|
|
95
|
+
if (!hasEmptyItem && (!this.maxItems || items.length < this.maxItems)) {
|
|
93
96
|
items.push(this.createEmptyItem());
|
|
94
97
|
}
|
|
95
98
|
}
|
|
@@ -111,6 +114,19 @@ export abstract class BaseListEditor<
|
|
|
111
114
|
fieldValue: any
|
|
112
115
|
) {
|
|
113
116
|
const updatedItems = [...this._items];
|
|
117
|
+
|
|
118
|
+
// If editing beyond the current array (auto-generated empty row), check maxItems
|
|
119
|
+
if (index >= this._items.length) {
|
|
120
|
+
if (this.maxItems && this._items.length >= this.maxItems) {
|
|
121
|
+
// Don't allow adding new items if we've reached maxItems
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// Extend the array to include the new item
|
|
125
|
+
while (updatedItems.length <= index) {
|
|
126
|
+
updatedItems.push(this.createEmptyItem());
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
114
130
|
const currentItem = updatedItems[index] || this.createEmptyItem();
|
|
115
131
|
|
|
116
132
|
updatedItems[index] = {
|