@nyaruka/temba-components 0.129.3 → 0.129.4
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/.eslintrc.js +1 -0
- package/.github/workflows/build.yml +135 -3
- package/CHANGELOG.md +18 -0
- package/demo/data/flows/sample-flow.json +110 -87
- package/demo/field-config-demo.html +135 -0
- package/dist/temba-components.js +1257 -675
- package/dist/temba-components.js.map +1 -1
- package/docs/ActionEditor-Migration.md +118 -0
- package/out-tsc/src/events.js.map +1 -1
- package/out-tsc/src/flow/{EditorNode.js → CanvasNode.js} +345 -42
- package/out-tsc/src/flow/CanvasNode.js.map +1 -0
- package/out-tsc/src/flow/Editor.js +107 -3
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +1200 -0
- package/out-tsc/src/flow/NodeEditor.js.map +1 -0
- package/out-tsc/src/flow/Plumber.js +0 -6
- package/out-tsc/src/flow/Plumber.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_groups.js +40 -0
- package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -0
- package/out-tsc/src/flow/actions/add_contact_urn.js +16 -0
- package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -0
- package/out-tsc/src/flow/actions/add_input_labels.js +11 -0
- package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -0
- package/out-tsc/src/flow/actions/call_classifier.js +11 -0
- package/out-tsc/src/flow/actions/call_classifier.js.map +1 -0
- package/out-tsc/src/flow/actions/call_llm.js +11 -0
- package/out-tsc/src/flow/actions/call_llm.js.map +1 -0
- package/out-tsc/src/flow/actions/call_resthook.js +11 -0
- package/out-tsc/src/flow/actions/call_resthook.js.map +1 -0
- package/out-tsc/src/flow/actions/call_webhook.js +122 -0
- package/out-tsc/src/flow/actions/call_webhook.js.map +1 -0
- package/out-tsc/src/flow/actions/enter_flow.js +14 -0
- package/out-tsc/src/flow/actions/enter_flow.js.map +1 -0
- package/out-tsc/src/flow/actions/open_ticket.js +11 -0
- package/out-tsc/src/flow/actions/open_ticket.js.map +1 -0
- package/out-tsc/src/flow/actions/play_audio.js +11 -0
- package/out-tsc/src/flow/actions/play_audio.js.map +1 -0
- package/out-tsc/src/flow/actions/remove_contact_groups.js +62 -0
- package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -0
- package/out-tsc/src/flow/actions/request_optin.js +11 -0
- package/out-tsc/src/flow/actions/request_optin.js.map +1 -0
- package/out-tsc/src/flow/actions/say_msg.js +11 -0
- package/out-tsc/src/flow/actions/say_msg.js.map +1 -0
- package/out-tsc/src/flow/actions/send_broadcast.js +33 -0
- package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -0
- package/out-tsc/src/flow/actions/send_email.js +56 -0
- package/out-tsc/src/flow/actions/send_email.js.map +1 -0
- package/out-tsc/src/flow/actions/send_msg.js +55 -0
- package/out-tsc/src/flow/actions/send_msg.js.map +1 -0
- package/out-tsc/src/flow/actions/set_contact_channel.js +12 -0
- package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -0
- package/out-tsc/src/flow/actions/set_contact_field.js +12 -0
- package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -0
- package/out-tsc/src/flow/actions/set_contact_language.js +10 -0
- package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -0
- package/out-tsc/src/flow/actions/set_contact_name.js +10 -0
- package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -0
- package/out-tsc/src/flow/actions/set_contact_status.js +10 -0
- package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -0
- package/out-tsc/src/flow/actions/set_run_result.js +10 -0
- package/out-tsc/src/flow/actions/set_run_result.js.map +1 -0
- package/out-tsc/src/flow/actions/split_by_expression_example.js +77 -0
- package/out-tsc/src/flow/actions/split_by_expression_example.js.map +1 -0
- package/out-tsc/src/flow/actions/start_session.js +11 -0
- package/out-tsc/src/flow/actions/start_session.js.map +1 -0
- package/out-tsc/src/flow/actions/transfer_airtime.js +11 -0
- package/out-tsc/src/flow/actions/transfer_airtime.js.map +1 -0
- package/out-tsc/src/flow/config.js +88 -193
- package/out-tsc/src/flow/config.js.map +1 -1
- package/out-tsc/src/flow/nodes/execute_actions.js +4 -0
- package/out-tsc/src/flow/nodes/execute_actions.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_airtime.js +9 -0
- package/out-tsc/src/flow/nodes/split_by_airtime.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_contact_field.js +7 -0
- package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_expression.js +7 -0
- package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_groups.js +7 -0
- package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_random.js +10 -0
- package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_run_result.js +7 -0
- package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_scheme.js +7 -0
- package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_subflow.js +9 -0
- package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_webhook.js +18 -0
- package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_audio.js +7 -0
- package/out-tsc/src/flow/nodes/wait_for_audio.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_digits.js +7 -0
- package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_image.js +7 -0
- package/out-tsc/src/flow/nodes/wait_for_image.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_location.js +7 -0
- package/out-tsc/src/flow/nodes/wait_for_location.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_menu.js +7 -0
- package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_response.js +7 -0
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_video.js +7 -0
- package/out-tsc/src/flow/nodes/wait_for_video.js.map +1 -0
- package/out-tsc/src/flow/types.js +79 -0
- package/out-tsc/src/flow/types.js.map +1 -0
- package/out-tsc/src/flow/utils.js +65 -0
- package/out-tsc/src/flow/utils.js.map +1 -0
- package/out-tsc/src/form/ArrayEditor.js +199 -0
- package/out-tsc/src/form/ArrayEditor.js.map +1 -0
- package/out-tsc/src/form/BaseListEditor.js +128 -0
- package/out-tsc/src/form/BaseListEditor.js.map +1 -0
- package/out-tsc/src/form/Checkbox.js +17 -2
- package/out-tsc/src/form/Checkbox.js.map +1 -1
- package/out-tsc/src/form/Completion.js +6 -0
- package/out-tsc/src/form/Completion.js.map +1 -1
- package/out-tsc/src/form/FormField.js +110 -11
- package/out-tsc/src/form/FormField.js.map +1 -1
- package/out-tsc/src/form/KeyValueEditor.js +223 -0
- package/out-tsc/src/form/KeyValueEditor.js.map +1 -0
- package/out-tsc/src/form/select/Select.js +77 -32
- package/out-tsc/src/form/select/Select.js.map +1 -1
- package/out-tsc/src/interfaces.js +6 -0
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +2 -76
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/temba-modules.js +9 -2
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/ActionHelper.js +116 -0
- package/out-tsc/test/ActionHelper.js.map +1 -0
- package/out-tsc/test/actions/add_contact_groups.test.js +66 -0
- package/out-tsc/test/actions/add_contact_groups.test.js.map +1 -0
- package/out-tsc/test/actions/remove_contact_groups.test.js +226 -0
- package/out-tsc/test/actions/remove_contact_groups.test.js.map +1 -0
- package/out-tsc/test/actions/send_email.test.js +160 -0
- package/out-tsc/test/actions/send_email.test.js.map +1 -0
- package/out-tsc/test/actions/send_msg.test.js +95 -0
- package/out-tsc/test/actions/send_msg.test.js.map +1 -0
- package/out-tsc/test/temba-action-editing-integration.test.js +183 -0
- package/out-tsc/test/temba-action-editing-integration.test.js.map +1 -0
- package/out-tsc/test/temba-checkbox.test.js +1 -1
- package/out-tsc/test/temba-checkbox.test.js.map +1 -1
- package/out-tsc/test/temba-field-config.test.js +133 -0
- package/out-tsc/test/temba-field-config.test.js.map +1 -0
- package/out-tsc/test/temba-flow-editor-node.test.js +14 -14
- package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
- package/out-tsc/test/temba-node-editor.test.js +283 -0
- package/out-tsc/test/temba-node-editor.test.js.map +1 -0
- package/out-tsc/test/temba-select.test.js +85 -0
- package/out-tsc/test/temba-select.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/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/add_contact_groups/render/descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/single-group.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/remove_contact_groups/render/cleanup-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/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_email/render/complex-business-email.png +0 -0
- package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
- package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
- package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
- package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
- package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
- package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
- package/screenshots/truth/actions/send_email/render/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/actions/send_msg/render/long-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/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/formfield/markdown-errors.png +0 -0
- package/screenshots/truth/formfield/plain-text-errors.png +0 -0
- package/screenshots/truth/formfield/widget-only-markdown-errors.png +0 -0
- package/screenshots/truth/integration/checkbox-markdown-errors.png +0 -0
- package/src/events.ts +1 -40
- package/src/flow/{EditorNode.ts → CanvasNode.ts} +424 -48
- package/src/flow/Editor.ts +140 -4
- package/src/flow/NodeEditor.ts +1443 -0
- package/src/flow/Plumber.ts +0 -9
- package/src/flow/actions/add_contact_groups.ts +42 -0
- package/src/flow/actions/add_contact_urn.ts +17 -0
- package/src/flow/actions/add_input_labels.ts +12 -0
- package/src/flow/actions/call_classifier.ts +12 -0
- package/src/flow/actions/call_llm.ts +12 -0
- package/src/flow/actions/call_resthook.ts +12 -0
- package/src/flow/actions/call_webhook.ts +133 -0
- package/src/flow/actions/enter_flow.ts +15 -0
- package/src/flow/actions/open_ticket.ts +12 -0
- package/src/flow/actions/play_audio.ts +12 -0
- package/src/flow/actions/remove_contact_groups.ts +66 -0
- package/src/flow/actions/request_optin.ts +12 -0
- package/src/flow/actions/say_msg.ts +12 -0
- package/src/flow/actions/send_broadcast.ts +35 -0
- package/src/flow/actions/send_email.ts +60 -0
- package/src/flow/actions/send_msg.ts +58 -0
- package/src/flow/actions/set_contact_channel.ts +13 -0
- package/src/flow/actions/set_contact_field.ts +13 -0
- package/src/flow/actions/set_contact_language.ts +11 -0
- package/src/flow/actions/set_contact_name.ts +11 -0
- package/src/flow/actions/set_contact_status.ts +11 -0
- package/src/flow/actions/set_run_result.ts +11 -0
- package/src/flow/actions/split_by_expression_example.ts +88 -0
- package/src/flow/actions/start_session.ts +12 -0
- package/src/flow/actions/transfer_airtime.ts +12 -0
- package/src/flow/config.ts +93 -232
- package/src/flow/nodes/execute_actions.ts +5 -0
- package/src/flow/nodes/split_by_airtime.ts +9 -0
- package/src/flow/nodes/split_by_contact_field.ts +7 -0
- package/src/flow/nodes/split_by_expression.ts +7 -0
- package/src/flow/nodes/split_by_groups.ts +7 -0
- package/src/flow/nodes/split_by_random.ts +10 -0
- package/src/flow/nodes/split_by_run_result.ts +7 -0
- package/src/flow/nodes/split_by_scheme.ts +7 -0
- package/src/flow/nodes/split_by_subflow.ts +9 -0
- package/src/flow/nodes/split_by_webhook.ts +19 -0
- package/src/flow/nodes/wait_for_audio.ts +7 -0
- package/src/flow/nodes/wait_for_digits.ts +7 -0
- package/src/flow/nodes/wait_for_image.ts +7 -0
- package/src/flow/nodes/wait_for_location.ts +7 -0
- package/src/flow/nodes/wait_for_menu.ts +7 -0
- package/src/flow/nodes/wait_for_response.ts +7 -0
- package/src/flow/nodes/wait_for_video.ts +7 -0
- package/src/flow/types.ts +352 -0
- package/src/flow/utils.ts +76 -0
- package/src/form/ArrayEditor.ts +240 -0
- package/src/form/BaseListEditor.ts +177 -0
- package/src/form/Checkbox.ts +22 -3
- package/src/form/Completion.ts +6 -0
- package/src/form/FormField.ts +115 -11
- package/src/form/KeyValueEditor.ts +251 -0
- package/src/form/select/Select.ts +89 -32
- package/src/interfaces.ts +7 -2
- package/src/live/ContactChat.ts +3 -97
- package/src/store/flow-definition.d.ts +6 -1
- package/static/api/contacts.json +30 -0
- package/static/api/groups.json +4 -426
- package/static/api/locations.json +24 -0
- package/static/api/media.json +5 -0
- package/static/api/optins.json +16 -0
- package/static/api/orgs.json +13 -0
- package/static/api/topics.json +21 -0
- package/static/api/users.json +26 -0
- package/static/css/temba-components.css +3 -6
- package/temba-modules.ts +9 -2
- package/test/ActionHelper.ts +142 -0
- package/test/actions/add_contact_groups.test.ts +89 -0
- package/test/actions/remove_contact_groups.test.ts +265 -0
- package/test/actions/send_email.test.ts +214 -0
- package/test/actions/send_msg.test.ts +130 -0
- package/test/temba-action-editing-integration.test.ts +240 -0
- package/test/temba-checkbox.test.ts +1 -1
- package/test/temba-field-config.test.ts +152 -0
- package/test/temba-flow-editor-node.test.ts +18 -18
- package/test/temba-node-editor.test.ts +353 -0
- package/test/temba-select.test.ts +127 -0
- package/test-assets/contacts/history.json +11 -33
- package/web-dev-server.config.mjs +34 -0
- package/.github/workflows/coverage.yml +0 -80
- package/demo/sticky-note-demo.html +0 -155
- package/out-tsc/src/flow/EditorNode.js.map +0 -1
- package/out-tsc/src/flow/render.js +0 -358
- package/out-tsc/src/flow/render.js.map +0 -1
- package/out-tsc/test/temba-flow-render.test.js +0 -794
- package/out-tsc/test/temba-flow-render.test.js.map +0 -1
- package/src/flow/render.ts +0 -443
- package/test/temba-flow-render.test.ts +0 -1003
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export const COLORS = {
|
|
2
|
+
send: '#3498db',
|
|
3
|
+
update: '#01c1af',
|
|
4
|
+
broadcast: '#8e5ea7',
|
|
5
|
+
call: '#e68628',
|
|
6
|
+
create: '#df419f',
|
|
7
|
+
save: '#1a777c',
|
|
8
|
+
split: '#aaaaaa',
|
|
9
|
+
execute: '#666666',
|
|
10
|
+
wait: '#4d7dad',
|
|
11
|
+
add: '#309c42',
|
|
12
|
+
remove: '#e74c3c'
|
|
13
|
+
};
|
|
14
|
+
// Default property type mappings
|
|
15
|
+
export function getDefaultComponent(value) {
|
|
16
|
+
if (typeof value === 'boolean') {
|
|
17
|
+
return 'temba-checkbox';
|
|
18
|
+
}
|
|
19
|
+
if (typeof value === 'number') {
|
|
20
|
+
return 'temba-textinput';
|
|
21
|
+
}
|
|
22
|
+
if (Array.isArray(value)) {
|
|
23
|
+
return 'temba-select'; // For arrays, use multi-select
|
|
24
|
+
}
|
|
25
|
+
// Default to text input for strings and unknown types
|
|
26
|
+
return 'temba-textinput';
|
|
27
|
+
}
|
|
28
|
+
// Get component properties for default mappings with proper typing
|
|
29
|
+
export function getDefaultComponentProps(value) {
|
|
30
|
+
if (typeof value === 'boolean') {
|
|
31
|
+
return {
|
|
32
|
+
widget: { type: 'temba-checkbox' }
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (typeof value === 'number') {
|
|
36
|
+
return {
|
|
37
|
+
widget: {
|
|
38
|
+
type: 'temba-textinput',
|
|
39
|
+
attributes: { type: 'number' }
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (Array.isArray(value)) {
|
|
44
|
+
if (value.length > 0 && typeof value[0] === 'string') {
|
|
45
|
+
return {
|
|
46
|
+
widget: {
|
|
47
|
+
type: 'temba-select',
|
|
48
|
+
attributes: { multi: true, tags: true }
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
widget: {
|
|
54
|
+
type: 'temba-select',
|
|
55
|
+
attributes: { multi: true }
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
widget: { type: 'temba-textinput' }
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// Type guard functions for working with WidgetConfig
|
|
64
|
+
export function isTextInputWidget(config) {
|
|
65
|
+
return config.type === 'temba-textinput';
|
|
66
|
+
}
|
|
67
|
+
export function isCompletionWidget(config) {
|
|
68
|
+
return config.type === 'temba-completion';
|
|
69
|
+
}
|
|
70
|
+
export function isSelectWidget(config) {
|
|
71
|
+
return config.type === 'temba-select';
|
|
72
|
+
}
|
|
73
|
+
export function isCheckboxWidget(config) {
|
|
74
|
+
return config.type === 'temba-checkbox';
|
|
75
|
+
}
|
|
76
|
+
export function isSliderWidget(config) {
|
|
77
|
+
return config.type === 'temba-slider';
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/flow/types.ts"],"names":[],"mappings":"AAiQA,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,SAAS;IACjB,SAAS,EAAE,SAAS;IACpB,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,SAAS;IACjB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,SAAS;IACf,GAAG,EAAE,SAAS;IACd,MAAM,EAAE,SAAS;CAClB,CAAC;AAEF,iCAAiC;AACjC,MAAM,UAAU,mBAAmB,CAAC,KAAU;IAC5C,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,iBAAiB,CAAC;IAC3B,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,cAAc,CAAC,CAAC,+BAA+B;IACxD,CAAC;IACD,sDAAsD;IACtD,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,wBAAwB,CAAC,KAAU;IACjD,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO;YACL,MAAM,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE;SACnC,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO;YACL,MAAM,EAAE;gBACN,IAAI,EAAE,iBAAiB;gBACvB,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;aAC/B;SACF,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;YACrD,OAAO;gBACL,MAAM,EAAE;oBACN,IAAI,EAAE,cAAc;oBACpB,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;iBACxC;aACF,CAAC;QACJ,CAAC;QACD,OAAO;YACL,MAAM,EAAE;gBACN,IAAI,EAAE,cAAc;gBACpB,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;aAC5B;SACF,CAAC;IACJ,CAAC;IACD,OAAO;QACL,MAAM,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;KACpC,CAAC;AACJ,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,iBAAiB,CAC/B,MAAoB;IAEpB,OAAO,MAAM,CAAC,IAAI,KAAK,iBAAiB,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,MAAoB;IAEpB,OAAO,MAAM,CAAC,IAAI,KAAK,kBAAkB,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,MAAoB;IAEpB,OAAO,MAAM,CAAC,IAAI,KAAK,cAAc,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,MAAoB;IAEpB,OAAO,MAAM,CAAC,IAAI,KAAK,gBAAgB,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,MAAoB;IAEpB,OAAO,MAAM,CAAC,IAAI,KAAK,cAAc,CAAC;AACxC,CAAC","sourcesContent":["import { TemplateResult } from 'lit-html';\nimport { Action } from '../store/flow-definition';\n\nexport interface ValidationResult {\n valid: boolean;\n errors: { [key: string]: string };\n}\n\n// Component attribute interfaces - these define what's allowed for each component type\nexport interface TextInputAttributes {\n type?: 'text' | 'email' | 'number' | 'url' | 'tel';\n placeholder?: string;\n clearable?: boolean;\n maxlength?: number;\n gsm?: boolean;\n autogrow?: boolean;\n textarea?: boolean;\n submitOnEnter?: boolean;\n}\n\nexport interface CompletionAttributes {\n placeholder?: string;\n clearable?: boolean;\n maxlength?: number;\n gsm?: boolean;\n autogrow?: boolean;\n textarea?: boolean;\n expressions?: string;\n counter?: string;\n minHeight?: number;\n}\n\nexport interface SelectAttributes {\n placeholder?: string;\n multi?: boolean;\n searchable?: boolean;\n tags?: boolean;\n emails?: boolean;\n clearable?: boolean;\n endpoint?: string;\n valueKey?: string;\n nameKey?: string;\n queryParam?: string;\n maxItems?: number;\n maxItemsText?: string;\n expressions?: string;\n options?: Array<{ name: string; value: any }>;\n sorted?: boolean;\n allowCreate?: boolean;\n jsonValue?: boolean;\n spaceSelect?: boolean;\n infoText?: string;\n}\n\nexport interface CheckboxAttributes {\n label?: string;\n size?: number;\n disabled?: boolean;\n animateChange?: string;\n}\n\nexport interface SliderAttributes {\n min?: number;\n max?: number;\n range?: boolean;\n}\n\n// Widget configuration using discriminated union for type safety\nexport type WidgetConfig =\n | { type: 'temba-textinput'; attributes?: TextInputAttributes }\n | { type: 'temba-completion'; attributes?: CompletionAttributes }\n | { type: 'temba-select'; attributes?: SelectAttributes }\n | { type: 'temba-checkbox'; attributes?: CheckboxAttributes }\n | { type: 'temba-slider'; attributes?: SliderAttributes }\n | { type: string; attributes?: { [key: string]: any } }; // Generic fallback\n\n// Property configuration with the clean structure you want\nexport interface PropertyConfig {\n // Form field metadata\n label?: string;\n helpText?: string;\n required?: boolean;\n maxLength?: number;\n minLength?: number;\n pattern?: string;\n\n // Widget configuration\n widget: WidgetConfig;\n\n // Conditional behavior based on other field values\n conditions?: {\n // When to show this field\n visible?: (formData: any) => boolean;\n\n // When this field is disabled\n disabled?: (formData: any) => boolean;\n };\n}\n\nexport interface NodeConfig {\n type: string;\n name?: string;\n color?: string;\n action?: ActionConfig;\n router?: {\n type: 'switch' | 'random';\n defaultCategory?: string;\n operand?: string;\n configurable?: boolean; // can the rules be configured in the UI\n rules?: {\n type: 'has_number_between' | 'has_string' | 'has_value' | 'has_not_value';\n arguments: string[];\n categoryName: string;\n }[];\n };\n properties?: { [key: string]: PropertyConfig };\n toFormData?: (node: any) => any;\n fromFormData?: (formData: any, originalNode: any) => any;\n}\n\n// New field configuration system for generic form generation\nexport interface BaseFieldConfig {\n label?: string;\n required?: boolean;\n evaluated?: boolean; // if this field supports expression evaluation\n dependsOn?: string[]; // fields this field depends on\n computeValue?: (\n values: Record<string, any>,\n currentValue: any,\n originalValues?: Record<string, any>\n ) => any;\n\n // Validation properties\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n helpText?: string;\n\n // Layout properties\n maxWidth?: string; // CSS max-width value (e.g., '200px', '50%', '10rem')\n\n // Conditional rendering\n conditions?: {\n visible?: (formData: Record<string, any>) => boolean;\n disabled?: (formData: Record<string, any>) => boolean;\n };\n}\n\nexport interface TextFieldConfig extends BaseFieldConfig {\n type: 'text';\n placeholder?: string;\n}\n\nexport interface TextareaFieldConfig extends BaseFieldConfig {\n type: 'textarea';\n placeholder?: string;\n rows?: number;\n minHeight?: number;\n}\n\nexport interface SelectFieldConfig extends BaseFieldConfig {\n type: 'select';\n options: string[] | { value: string; label: string }[];\n multi?: boolean;\n searchable?: boolean;\n tags?: boolean;\n placeholder?: string;\n maxItems?: number;\n valueKey?: string;\n nameKey?: string;\n endpoint?: string;\n emails?: boolean;\n}\n\nexport interface KeyValueFieldConfig extends BaseFieldConfig {\n type: 'key-value';\n sortable?: boolean;\n keyPlaceholder?: string;\n valuePlaceholder?: string;\n minRows?: number;\n}\n\nexport interface ArrayFieldConfig extends BaseFieldConfig {\n type: 'array';\n itemConfig: Record<string, FieldConfig>;\n sortable?: boolean;\n minItems?: number;\n maxItems?: number;\n itemLabel?: string;\n onItemChange?: (\n itemIndex: number,\n field: string,\n value: any,\n allItems: any[]\n ) => any[];\n}\n\nexport interface CheckboxFieldConfig extends BaseFieldConfig {\n type: 'checkbox';\n size?: number;\n animateChange?: string;\n}\n\nexport type FieldConfig =\n | TextFieldConfig\n | TextareaFieldConfig\n | SelectFieldConfig\n | KeyValueFieldConfig\n | ArrayFieldConfig\n | CheckboxFieldConfig;\n\n// Layout configurations for better form organization\n// Recursive layout system - any layout item can contain other layout items\n\nexport interface FieldItemConfig {\n type: 'field';\n field: string; // field name to render\n}\n\nexport interface RowLayoutConfig {\n type: 'row';\n items: LayoutItem[]; // can contain fields, groups, or other rows\n gap?: string; // CSS gap value, defaults to '1rem'\n}\n\nexport interface GroupLayoutConfig {\n type: 'group';\n label: string;\n items: LayoutItem[]; // can contain fields, rows, or other groups\n collapsible?: boolean;\n collapsed?: boolean; // initial state if collapsible\n helpText?: string;\n}\n\nexport type LayoutItem =\n | FieldItemConfig\n | RowLayoutConfig\n | GroupLayoutConfig\n | string; // string is shorthand for field\n\nexport interface ActionConfig {\n name: string;\n color: string;\n evaluated?: string[];\n render?: (node: any, action: any) => TemplateResult;\n\n form?: Record<string, FieldConfig>;\n layout?: LayoutItem[]; // optional layout configuration - array of layout items\n\n // Action editor configuration (legacy)\n // Form-level transformations\n toFormData?: (action: Action) => any;\n fromFormData?: (formData: any) => Action;\n\n validate?: (action: Action) => ValidationResult;\n}\n\nexport const COLORS = {\n send: '#3498db',\n update: '#01c1af',\n broadcast: '#8e5ea7',\n call: '#e68628',\n create: '#df419f',\n save: '#1a777c',\n split: '#aaaaaa',\n execute: '#666666',\n wait: '#4d7dad',\n add: '#309c42',\n remove: '#e74c3c'\n};\n\n// Default property type mappings\nexport function getDefaultComponent(value: any): WidgetConfig['type'] {\n if (typeof value === 'boolean') {\n return 'temba-checkbox';\n }\n if (typeof value === 'number') {\n return 'temba-textinput';\n }\n if (Array.isArray(value)) {\n return 'temba-select'; // For arrays, use multi-select\n }\n // Default to text input for strings and unknown types\n return 'temba-textinput';\n}\n\n// Get component properties for default mappings with proper typing\nexport function getDefaultComponentProps(value: any): PropertyConfig {\n if (typeof value === 'boolean') {\n return {\n widget: { type: 'temba-checkbox' }\n };\n }\n if (typeof value === 'number') {\n return {\n widget: {\n type: 'temba-textinput',\n attributes: { type: 'number' }\n }\n };\n }\n if (Array.isArray(value)) {\n if (value.length > 0 && typeof value[0] === 'string') {\n return {\n widget: {\n type: 'temba-select',\n attributes: { multi: true, tags: true }\n }\n };\n }\n return {\n widget: {\n type: 'temba-select',\n attributes: { multi: true }\n }\n };\n }\n return {\n widget: { type: 'temba-textinput' }\n };\n}\n\n// Type guard functions for working with WidgetConfig\nexport function isTextInputWidget(\n config: WidgetConfig\n): config is { type: 'temba-textinput'; attributes?: TextInputAttributes } {\n return config.type === 'temba-textinput';\n}\n\nexport function isCompletionWidget(\n config: WidgetConfig\n): config is { type: 'temba-completion'; attributes?: CompletionAttributes } {\n return config.type === 'temba-completion';\n}\n\nexport function isSelectWidget(\n config: WidgetConfig\n): config is { type: 'temba-select'; attributes?: SelectAttributes } {\n return config.type === 'temba-select';\n}\n\nexport function isCheckboxWidget(\n config: WidgetConfig\n): config is { type: 'temba-checkbox'; attributes?: CheckboxAttributes } {\n return config.type === 'temba-checkbox';\n}\n\nexport function isSliderWidget(\n config: WidgetConfig\n): config is { type: 'slider'; attributes?: SliderAttributes } {\n return config.type === 'temba-slider';\n}\n"]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { html } from 'lit-html';
|
|
2
|
+
/**
|
|
3
|
+
* Renders a single line item with optional icon
|
|
4
|
+
*/
|
|
5
|
+
export const renderLineItem = (name, icon) => {
|
|
6
|
+
return html `<div style="display:flex;items-align:center;">
|
|
7
|
+
${icon
|
|
8
|
+
? html `<temba-icon name=${icon} style="margin-right:0.5em"></temba-icon>`
|
|
9
|
+
: null}
|
|
10
|
+
<div
|
|
11
|
+
style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 250px;"
|
|
12
|
+
>
|
|
13
|
+
${name}
|
|
14
|
+
</div>
|
|
15
|
+
</div>`;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Renders a list of named objects with optional icon, showing up to 3 items
|
|
19
|
+
* with a "+X more" indicator if there are more items
|
|
20
|
+
*/
|
|
21
|
+
export const renderNamedObjects = (assets, icon) => {
|
|
22
|
+
return renderStringList(assets.map((asset) => asset.name), icon);
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Renders a list of strings with optional icon, showing up to 3 items
|
|
26
|
+
* with a "+X more" indicator if there are more items
|
|
27
|
+
*/
|
|
28
|
+
export const renderStringList = (items, icon) => {
|
|
29
|
+
const itemElements = [];
|
|
30
|
+
const maxDisplay = 3;
|
|
31
|
+
// Show up to 3 items, or all 4 if exactly 4 items
|
|
32
|
+
const displayCount = items.length === 4 ? 4 : Math.min(maxDisplay, items.length);
|
|
33
|
+
for (let i = 0; i < displayCount; i++) {
|
|
34
|
+
const item = items[i];
|
|
35
|
+
itemElements.push(renderLineItem(item, icon));
|
|
36
|
+
}
|
|
37
|
+
// Add "+X more" if there are more than 3 items (and not exactly 4)
|
|
38
|
+
if (items.length > maxDisplay && items.length !== 4) {
|
|
39
|
+
const remainingCount = items.length - maxDisplay;
|
|
40
|
+
itemElements.push(html `<div
|
|
41
|
+
style="display:flex;items-align:center;margin-top:0.2em;"
|
|
42
|
+
>
|
|
43
|
+
${icon
|
|
44
|
+
? html `<div style="margin-right:0.4em; width: 1em;"></div>` // spacing placeholder
|
|
45
|
+
: null}
|
|
46
|
+
<div style="font-size:0.8em">+${remainingCount} more</div>
|
|
47
|
+
</div>`);
|
|
48
|
+
}
|
|
49
|
+
return itemElements;
|
|
50
|
+
};
|
|
51
|
+
// URN scheme mapping for user-friendly display
|
|
52
|
+
export const urnSchemeMap = {
|
|
53
|
+
tel: 'Phone Number',
|
|
54
|
+
email: 'Email',
|
|
55
|
+
twitter: 'Twitter',
|
|
56
|
+
facebook: 'Facebook',
|
|
57
|
+
telegram: 'Telegram',
|
|
58
|
+
whatsapp: 'WhatsApp',
|
|
59
|
+
viber: 'Viber',
|
|
60
|
+
line: 'Line',
|
|
61
|
+
discord: 'Discord',
|
|
62
|
+
slack: 'Slack',
|
|
63
|
+
external: 'External ID'
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/flow/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAGhC;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAY,EAAE,IAAa,EAAE,EAAE;IAC5D,OAAO,IAAI,CAAA;MACP,IAAI;QACJ,CAAC,CAAC,IAAI,CAAA,oBAAoB,IAAI,2CAA2C;QACzE,CAAC,CAAC,IAAI;;;;QAIJ,IAAI;;SAEH,CAAC;AACV,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,MAAqB,EAAE,IAAa,EAAE,EAAE;IACzE,OAAO,gBAAgB,CACrB,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EACjC,IAAI,CACL,CAAC;AACJ,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAe,EAAE,IAAa,EAAE,EAAE;IACjE,MAAM,YAAY,GAAG,EAAE,CAAC;IACxB,MAAM,UAAU,GAAG,CAAC,CAAC;IAErB,kDAAkD;IAClD,MAAM,YAAY,GAChB,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAE9D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IAChD,CAAC;IAED,mEAAmE;IACnE,IAAI,KAAK,CAAC,MAAM,GAAG,UAAU,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC;QACjD,YAAY,CAAC,IAAI,CAAC,IAAI,CAAA;;;QAGlB,IAAI;YACJ,CAAC,CAAC,IAAI,CAAA,qDAAqD,CAAC,sBAAsB;YAClF,CAAC,CAAC,IAAI;sCACwB,cAAc;WACzC,CAAC,CAAC;IACX,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC,CAAC;AAEF,+CAA+C;AAC/C,MAAM,CAAC,MAAM,YAAY,GAA2B;IAClD,GAAG,EAAE,cAAc;IACnB,KAAK,EAAE,OAAO;IACd,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,UAAU;IACpB,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;IACd,QAAQ,EAAE,aAAa;CACxB,CAAC","sourcesContent":["import { html } from 'lit-html';\nimport { NamedObject } from '../store/flow-definition';\n\n/**\n * Renders a single line item with optional icon\n */\nexport const renderLineItem = (name: string, icon?: string) => {\n return html`<div style=\"display:flex;items-align:center;\">\n ${icon\n ? html`<temba-icon name=${icon} style=\"margin-right:0.5em\"></temba-icon>`\n : null}\n <div\n style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 250px;\"\n >\n ${name}\n </div>\n </div>`;\n};\n\n/**\n * Renders a list of named objects with optional icon, showing up to 3 items\n * with a \"+X more\" indicator if there are more items\n */\nexport const renderNamedObjects = (assets: NamedObject[], icon?: string) => {\n return renderStringList(\n assets.map((asset) => asset.name),\n icon\n );\n};\n\n/**\n * Renders a list of strings with optional icon, showing up to 3 items\n * with a \"+X more\" indicator if there are more items\n */\nexport const renderStringList = (items: string[], icon?: string) => {\n const itemElements = [];\n const maxDisplay = 3;\n\n // Show up to 3 items, or all 4 if exactly 4 items\n const displayCount =\n items.length === 4 ? 4 : Math.min(maxDisplay, items.length);\n\n for (let i = 0; i < displayCount; i++) {\n const item = items[i];\n itemElements.push(renderLineItem(item, icon));\n }\n\n // Add \"+X more\" if there are more than 3 items (and not exactly 4)\n if (items.length > maxDisplay && items.length !== 4) {\n const remainingCount = items.length - maxDisplay;\n itemElements.push(html`<div\n style=\"display:flex;items-align:center;margin-top:0.2em;\"\n >\n ${icon\n ? html`<div style=\"margin-right:0.4em; width: 1em;\"></div>` // spacing placeholder\n : null}\n <div style=\"font-size:0.8em\">+${remainingCount} more</div>\n </div>`);\n }\n return itemElements;\n};\n\n// URN scheme mapping for user-friendly display\nexport const urnSchemeMap: Record<string, string> = {\n tel: 'Phone Number',\n email: 'Email',\n twitter: 'Twitter',\n facebook: 'Facebook',\n telegram: 'Telegram',\n whatsapp: 'WhatsApp',\n viber: 'Viber',\n line: 'Line',\n discord: 'Discord',\n slack: 'Slack',\n external: 'External ID'\n};\n"]}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { html, css } from 'lit';
|
|
3
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
4
|
+
import { BaseListEditor } from './BaseListEditor';
|
|
5
|
+
let TembaArrayEditor = class TembaArrayEditor extends BaseListEditor {
|
|
6
|
+
constructor() {
|
|
7
|
+
super();
|
|
8
|
+
this.itemConfig = {};
|
|
9
|
+
this.itemLabel = 'Item';
|
|
10
|
+
this._items = [];
|
|
11
|
+
}
|
|
12
|
+
// External API
|
|
13
|
+
get value() {
|
|
14
|
+
return [...this._items];
|
|
15
|
+
}
|
|
16
|
+
set value(newValue) {
|
|
17
|
+
this._items = newValue || [];
|
|
18
|
+
this.requestUpdate();
|
|
19
|
+
}
|
|
20
|
+
// Implement abstract methods
|
|
21
|
+
isEmptyItem(item) {
|
|
22
|
+
return Object.values(item).every((value) => value === undefined || value === null || value === '');
|
|
23
|
+
}
|
|
24
|
+
createEmptyItem() {
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
handleFieldChange(itemIndex, fieldName, newValue) {
|
|
28
|
+
let updatedItems;
|
|
29
|
+
if (this.onItemChange) {
|
|
30
|
+
updatedItems = this.onItemChange(itemIndex, fieldName, newValue, this._items);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
updatedItems = [...this._items];
|
|
34
|
+
updatedItems[itemIndex] = {
|
|
35
|
+
...updatedItems[itemIndex],
|
|
36
|
+
[fieldName]: newValue
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
this.updateValue(updatedItems);
|
|
40
|
+
}
|
|
41
|
+
computeFieldValue(itemIndex, fieldName, config) {
|
|
42
|
+
const item = this._items[itemIndex] || {};
|
|
43
|
+
const currentValue = item[fieldName];
|
|
44
|
+
if (config.computeValue) {
|
|
45
|
+
return config.computeValue(item, currentValue);
|
|
46
|
+
}
|
|
47
|
+
return currentValue;
|
|
48
|
+
}
|
|
49
|
+
renderField(itemIndex, fieldName, config) {
|
|
50
|
+
const computedValue = this.computeFieldValue(itemIndex, fieldName, config);
|
|
51
|
+
switch (config.type) {
|
|
52
|
+
case 'text':
|
|
53
|
+
return html `<temba-textinput
|
|
54
|
+
.value=${computedValue || ''}
|
|
55
|
+
.placeholder=${config.placeholder}
|
|
56
|
+
@change=${(e) => this.handleFieldChange(itemIndex, fieldName, e.target.value)}
|
|
57
|
+
></temba-textinput>`;
|
|
58
|
+
case 'textarea':
|
|
59
|
+
return html `<temba-textinput
|
|
60
|
+
.value=${computedValue || ''}
|
|
61
|
+
.placeholder=${config.placeholder}
|
|
62
|
+
textarea
|
|
63
|
+
.rows=${config.rows || 3}
|
|
64
|
+
@change=${(e) => this.handleFieldChange(itemIndex, fieldName, e.target.value)}
|
|
65
|
+
></temba-textinput>`;
|
|
66
|
+
case 'select':
|
|
67
|
+
return html `<temba-select
|
|
68
|
+
.value=${computedValue || ''}
|
|
69
|
+
.options=${config.options}
|
|
70
|
+
@change=${(e) => this.handleFieldChange(itemIndex, fieldName, e.target.value)}
|
|
71
|
+
></temba-select>`;
|
|
72
|
+
default:
|
|
73
|
+
return html `<span>Unsupported field type: ${config.type}</span>`;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
renderItem(item, index) {
|
|
77
|
+
const canRemove = this.canRemoveItem(index);
|
|
78
|
+
return html `
|
|
79
|
+
<div class="array-item">
|
|
80
|
+
<div class="item-header">
|
|
81
|
+
<span class="item-title">${this.itemLabel} ${index + 1}</span>
|
|
82
|
+
${canRemove
|
|
83
|
+
? html `
|
|
84
|
+
<button
|
|
85
|
+
@click=${() => this.removeItem(index)}
|
|
86
|
+
class="remove-btn"
|
|
87
|
+
>
|
|
88
|
+
Remove
|
|
89
|
+
</button>
|
|
90
|
+
`
|
|
91
|
+
: ''}
|
|
92
|
+
</div>
|
|
93
|
+
<div class="item-fields">
|
|
94
|
+
${Object.entries(this.itemConfig).map(([fieldName, config]) => html `
|
|
95
|
+
<div class="field">
|
|
96
|
+
<label>${config.label}${config.required ? ' *' : ''}</label>
|
|
97
|
+
${this.renderField(index, fieldName, config)}
|
|
98
|
+
</div>
|
|
99
|
+
`)}
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
`;
|
|
103
|
+
}
|
|
104
|
+
getContainerClass() {
|
|
105
|
+
return 'array-editor';
|
|
106
|
+
}
|
|
107
|
+
renderAddButton() {
|
|
108
|
+
return html `
|
|
109
|
+
<button class="add-btn" @click=${() => this.addItem()}>
|
|
110
|
+
Add ${this.itemLabel}
|
|
111
|
+
</button>
|
|
112
|
+
`;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
TembaArrayEditor.styles = css `
|
|
116
|
+
.array-editor {
|
|
117
|
+
border: 1px solid #e0e0e0;
|
|
118
|
+
border-radius: 6px;
|
|
119
|
+
padding: 16px;
|
|
120
|
+
background: #fafafa;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.array-item {
|
|
124
|
+
border: 1px solid #d0d0d0;
|
|
125
|
+
border-radius: 4px;
|
|
126
|
+
padding: 16px;
|
|
127
|
+
margin-bottom: 12px;
|
|
128
|
+
background: white;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.item-header {
|
|
132
|
+
display: flex;
|
|
133
|
+
justify-content: space-between;
|
|
134
|
+
align-items: center;
|
|
135
|
+
margin-bottom: 12px;
|
|
136
|
+
padding-bottom: 8px;
|
|
137
|
+
border-bottom: 1px solid #eee;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.item-title {
|
|
141
|
+
font-weight: 600;
|
|
142
|
+
color: #333;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.item-fields {
|
|
146
|
+
display: grid;
|
|
147
|
+
gap: 12px;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.field label {
|
|
151
|
+
display: block;
|
|
152
|
+
margin-bottom: 4px;
|
|
153
|
+
font-weight: 500;
|
|
154
|
+
color: #555;
|
|
155
|
+
font-size: 14px;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.add-btn,
|
|
159
|
+
.remove-btn {
|
|
160
|
+
padding: 8px 16px;
|
|
161
|
+
border: 1px solid #ccc;
|
|
162
|
+
border-radius: 4px;
|
|
163
|
+
background: white;
|
|
164
|
+
cursor: pointer;
|
|
165
|
+
font-size: 14px;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.add-btn:hover,
|
|
169
|
+
.remove-btn:hover {
|
|
170
|
+
background: #f8f8f8;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.remove-btn {
|
|
174
|
+
background: #fff5f5;
|
|
175
|
+
border-color: #fecaca;
|
|
176
|
+
color: #dc2626;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.remove-btn:hover {
|
|
180
|
+
background: #fef2f2;
|
|
181
|
+
}
|
|
182
|
+
`;
|
|
183
|
+
__decorate([
|
|
184
|
+
property({ type: Object })
|
|
185
|
+
], TembaArrayEditor.prototype, "itemConfig", void 0);
|
|
186
|
+
__decorate([
|
|
187
|
+
property({ type: String })
|
|
188
|
+
], TembaArrayEditor.prototype, "itemLabel", void 0);
|
|
189
|
+
__decorate([
|
|
190
|
+
property({ type: Function })
|
|
191
|
+
], TembaArrayEditor.prototype, "onItemChange", void 0);
|
|
192
|
+
__decorate([
|
|
193
|
+
property({ type: Array })
|
|
194
|
+
], TembaArrayEditor.prototype, "value", null);
|
|
195
|
+
TembaArrayEditor = __decorate([
|
|
196
|
+
customElement('temba-array-editor')
|
|
197
|
+
], TembaArrayEditor);
|
|
198
|
+
export { TembaArrayEditor };
|
|
199
|
+
//# sourceMappingURL=ArrayEditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ArrayEditor.js","sourceRoot":"","sources":["../../../src/form/ArrayEditor.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css, TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { FieldConfig } from '../flow/types';\nimport { BaseListEditor, ListItem } from './BaseListEditor';\n\n@customElement('temba-array-editor')\nexport class TembaArrayEditor extends BaseListEditor<ListItem> {\n @property({ type: Object })\n itemConfig: Record<string, FieldConfig> = {};\n\n @property({ type: String })\n itemLabel = 'Item';\n\n @property({ type: Function })\n onItemChange?: (\n itemIndex: number,\n field: string,\n value: any,\n allItems: any[]\n ) => any[];\n\n constructor() {\n super();\n this._items = [];\n }\n\n // External API\n @property({ type: Array })\n get value(): any[] {\n return [...this._items];\n }\n\n set value(newValue: any[]) {\n this._items = newValue || [];\n this.requestUpdate();\n }\n\n // Implement abstract methods\n isEmptyItem(item: ListItem): boolean {\n return Object.values(item).every(\n (value) => value === undefined || value === null || value === ''\n );\n }\n\n createEmptyItem(): ListItem {\n return {};\n }\n\n protected handleFieldChange(\n itemIndex: number,\n fieldName: string,\n newValue: any\n ) {\n let updatedItems: any[];\n\n if (this.onItemChange) {\n updatedItems = this.onItemChange(\n itemIndex,\n fieldName,\n newValue,\n this._items\n );\n } else {\n updatedItems = [...this._items];\n updatedItems[itemIndex] = {\n ...updatedItems[itemIndex],\n [fieldName]: newValue\n };\n }\n\n this.updateValue(updatedItems);\n }\n\n private computeFieldValue(\n itemIndex: number,\n fieldName: string,\n config: FieldConfig\n ): any {\n const item = this._items[itemIndex] || {};\n const currentValue = item[fieldName];\n\n if (config.computeValue) {\n return config.computeValue(item, currentValue);\n }\n\n return currentValue;\n }\n\n private renderField(\n itemIndex: number,\n fieldName: string,\n config: FieldConfig\n ): TemplateResult {\n const computedValue = this.computeFieldValue(itemIndex, fieldName, config);\n\n switch (config.type) {\n case 'text':\n return html`<temba-textinput\n .value=${computedValue || ''}\n .placeholder=${config.placeholder}\n @change=${(e: any) =>\n this.handleFieldChange(itemIndex, fieldName, e.target.value)}\n ></temba-textinput>`;\n\n case 'textarea':\n return html`<temba-textinput\n .value=${computedValue || ''}\n .placeholder=${config.placeholder}\n textarea\n .rows=${config.rows || 3}\n @change=${(e: any) =>\n this.handleFieldChange(itemIndex, fieldName, e.target.value)}\n ></temba-textinput>`;\n\n case 'select':\n return html`<temba-select\n .value=${computedValue || ''}\n .options=${config.options}\n @change=${(e: any) =>\n this.handleFieldChange(itemIndex, fieldName, e.target.value)}\n ></temba-select>`;\n\n default:\n return html`<span>Unsupported field type: ${config.type}</span>`;\n }\n }\n\n renderItem(item: ListItem, index: number): TemplateResult {\n const canRemove = this.canRemoveItem(index);\n\n return html`\n <div class=\"array-item\">\n <div class=\"item-header\">\n <span class=\"item-title\">${this.itemLabel} ${index + 1}</span>\n ${canRemove\n ? html`\n <button\n @click=${() => this.removeItem(index)}\n class=\"remove-btn\"\n >\n Remove\n </button>\n `\n : ''}\n </div>\n <div class=\"item-fields\">\n ${Object.entries(this.itemConfig).map(\n ([fieldName, config]) => html`\n <div class=\"field\">\n <label>${config.label}${config.required ? ' *' : ''}</label>\n ${this.renderField(index, fieldName, config)}\n </div>\n `\n )}\n </div>\n </div>\n `;\n }\n\n protected getContainerClass(): string {\n return 'array-editor';\n }\n\n protected renderAddButton(): TemplateResult {\n return html`\n <button class=\"add-btn\" @click=${() => this.addItem()}>\n Add ${this.itemLabel}\n </button>\n `;\n }\n\n static styles = css`\n .array-editor {\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n padding: 16px;\n background: #fafafa;\n }\n\n .array-item {\n border: 1px solid #d0d0d0;\n border-radius: 4px;\n padding: 16px;\n margin-bottom: 12px;\n background: white;\n }\n\n .item-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 12px;\n padding-bottom: 8px;\n border-bottom: 1px solid #eee;\n }\n\n .item-title {\n font-weight: 600;\n color: #333;\n }\n\n .item-fields {\n display: grid;\n gap: 12px;\n }\n\n .field label {\n display: block;\n margin-bottom: 4px;\n font-weight: 500;\n color: #555;\n font-size: 14px;\n }\n\n .add-btn,\n .remove-btn {\n padding: 8px 16px;\n border: 1px solid #ccc;\n border-radius: 4px;\n background: white;\n cursor: pointer;\n font-size: 14px;\n }\n\n .add-btn:hover,\n .remove-btn:hover {\n background: #f8f8f8;\n }\n\n .remove-btn {\n background: #fff5f5;\n border-color: #fecaca;\n color: #dc2626;\n }\n\n .remove-btn:hover {\n background: #fef2f2;\n }\n `;\n}\n"]}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { LitElement, html } from 'lit';
|
|
3
|
+
import { property } from 'lit/decorators.js';
|
|
4
|
+
export class BaseListEditor extends LitElement {
|
|
5
|
+
constructor() {
|
|
6
|
+
super(...arguments);
|
|
7
|
+
this._items = [];
|
|
8
|
+
this.minItems = 0;
|
|
9
|
+
this.maintainEmptyItem = false;
|
|
10
|
+
}
|
|
11
|
+
// Optional methods that subclasses can override
|
|
12
|
+
getContainerClass() {
|
|
13
|
+
return 'base-list-editor';
|
|
14
|
+
}
|
|
15
|
+
renderAddButton() {
|
|
16
|
+
return html `
|
|
17
|
+
<button class="add-btn" @click=${() => this.addItem()}>Add Item</button>
|
|
18
|
+
`;
|
|
19
|
+
}
|
|
20
|
+
shouldShowAddButton() {
|
|
21
|
+
return (!this.maintainEmptyItem &&
|
|
22
|
+
(!this.maxItems || this._items.length < this.maxItems));
|
|
23
|
+
}
|
|
24
|
+
render() {
|
|
25
|
+
const items = this.displayItems;
|
|
26
|
+
return html `
|
|
27
|
+
<div class=${this.getContainerClass()}>
|
|
28
|
+
<div
|
|
29
|
+
class="list-items"
|
|
30
|
+
style="gap: 8px; display: grid; grid-template-columns: 1fr;"
|
|
31
|
+
>
|
|
32
|
+
${items.map((item, index) => this.renderItem(item, index))}
|
|
33
|
+
</div>
|
|
34
|
+
${this.shouldShowAddButton() ? this.renderAddButton() : ''}
|
|
35
|
+
</div>
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
// Optional method for cleaning items before emission (can return any type)
|
|
39
|
+
cleanItems(items) {
|
|
40
|
+
if (!this.maintainEmptyItem) {
|
|
41
|
+
return items;
|
|
42
|
+
}
|
|
43
|
+
// Filter out empty items for the emitted value
|
|
44
|
+
return items.filter((item) => !this.isEmptyItem(item));
|
|
45
|
+
}
|
|
46
|
+
// Get the items to display (may include empty items for UI)
|
|
47
|
+
get displayItems() {
|
|
48
|
+
const items = [...this._items];
|
|
49
|
+
if (this.maintainEmptyItem) {
|
|
50
|
+
const hasEmptyItem = items.some((item) => this.isEmptyItem(item));
|
|
51
|
+
if (!hasEmptyItem) {
|
|
52
|
+
items.push(this.createEmptyItem());
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return items;
|
|
56
|
+
}
|
|
57
|
+
// Handle changes to an item
|
|
58
|
+
handleItemChange(index, newItem) {
|
|
59
|
+
const updatedItems = [...this._items];
|
|
60
|
+
updatedItems[index] = newItem;
|
|
61
|
+
this.updateValue(updatedItems);
|
|
62
|
+
}
|
|
63
|
+
// Handle field changes within an item (for complex items)
|
|
64
|
+
handleFieldChange(index, fieldName, fieldValue) {
|
|
65
|
+
const updatedItems = [...this._items];
|
|
66
|
+
const currentItem = updatedItems[index] || this.createEmptyItem();
|
|
67
|
+
updatedItems[index] = {
|
|
68
|
+
...currentItem,
|
|
69
|
+
[fieldName]: fieldValue
|
|
70
|
+
};
|
|
71
|
+
this.updateValue(updatedItems);
|
|
72
|
+
}
|
|
73
|
+
// Add a new item
|
|
74
|
+
addItem(item) {
|
|
75
|
+
if (this.maxItems && this._items.length >= this.maxItems) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const newItem = item || this.createEmptyItem();
|
|
79
|
+
const updatedItems = [...this._items, newItem];
|
|
80
|
+
this.updateValue(updatedItems);
|
|
81
|
+
}
|
|
82
|
+
// Remove an item
|
|
83
|
+
removeItem(index) {
|
|
84
|
+
if (this._items.length <= this.minItems) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const updatedItems = this._items.filter((_, i) => i !== index);
|
|
88
|
+
this.updateValue(updatedItems);
|
|
89
|
+
}
|
|
90
|
+
// Check if an item can be removed
|
|
91
|
+
canRemoveItem(index) {
|
|
92
|
+
const item = this.displayItems[index];
|
|
93
|
+
// Can't remove if it would go below minimum
|
|
94
|
+
if (this._items.length <= this.minItems) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
// Can't remove empty items if we're maintaining them
|
|
98
|
+
if (this.maintainEmptyItem && this.isEmptyItem(item)) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
// Update the value and emit change event
|
|
104
|
+
updateValue(newValue) {
|
|
105
|
+
this._items = newValue;
|
|
106
|
+
this.dispatchEvent(new CustomEvent('change', {
|
|
107
|
+
detail: { value: this.cleanItems(newValue) },
|
|
108
|
+
bubbles: true
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
// Utility method for subclasses to check if two items are equal
|
|
112
|
+
itemsEqual(item1, item2) {
|
|
113
|
+
return JSON.stringify(item1) === JSON.stringify(item2);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
__decorate([
|
|
117
|
+
property({ attribute: false })
|
|
118
|
+
], BaseListEditor.prototype, "_items", void 0);
|
|
119
|
+
__decorate([
|
|
120
|
+
property({ type: Number })
|
|
121
|
+
], BaseListEditor.prototype, "minItems", void 0);
|
|
122
|
+
__decorate([
|
|
123
|
+
property({ type: Number })
|
|
124
|
+
], BaseListEditor.prototype, "maxItems", void 0);
|
|
125
|
+
__decorate([
|
|
126
|
+
property({ type: Boolean })
|
|
127
|
+
], BaseListEditor.prototype, "maintainEmptyItem", void 0);
|
|
128
|
+
//# sourceMappingURL=BaseListEditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseListEditor.js","sourceRoot":"","sources":["../../../src/form/BaseListEditor.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, TemplateResult, html } from 'lit';\nimport { property } from 'lit/decorators.js';\n\nexport interface ListItem {\n [key: string]: any;\n}\n\nexport interface ListEditorConfig {\n // Determines if empty items should be automatically maintained\n maintainEmptyItem?: boolean;\n // Function to check if an item is considered empty\n isEmptyItem?: (item: ListItem) => boolean;\n // Function to create a new empty item\n createEmptyItem?: () => ListItem;\n // Function to clean items before emitting (e.g., filter out empty items)\n cleanItems?: (items: ListItem[]) => ListItem[];\n // Minimum number of items to maintain\n minItems?: number;\n // Maximum number of items allowed\n maxItems?: number;\n}\n\nexport abstract class BaseListEditor<\n T extends ListItem = ListItem\n> extends LitElement {\n @property({ attribute: false })\n protected _items: T[] = [];\n\n @property({ type: Number })\n minItems = 0;\n\n @property({ type: Number })\n maxItems?: number;\n\n @property({ type: Boolean })\n maintainEmptyItem = false;\n\n // Abstract methods that must be implemented by subclasses\n abstract isEmptyItem(item: T): boolean;\n abstract createEmptyItem(): T;\n abstract renderItem(item: T, index: number): TemplateResult;\n\n // Optional methods that subclasses can override\n protected getContainerClass(): string {\n return 'base-list-editor';\n }\n\n protected renderAddButton(): TemplateResult {\n return html`\n <button class=\"add-btn\" @click=${() => this.addItem()}>Add Item</button>\n `;\n }\n\n protected shouldShowAddButton(): boolean {\n return (\n !this.maintainEmptyItem &&\n (!this.maxItems || this._items.length < this.maxItems)\n );\n }\n\n render(): TemplateResult {\n const items = this.displayItems;\n\n return html`\n <div class=${this.getContainerClass()}>\n <div\n class=\"list-items\"\n style=\"gap: 8px; display: grid; grid-template-columns: 1fr;\"\n >\n ${items.map((item, index) => this.renderItem(item, index))}\n </div>\n ${this.shouldShowAddButton() ? this.renderAddButton() : ''}\n </div>\n `;\n }\n\n // Optional method for cleaning items before emission (can return any type)\n protected cleanItems(items: T[]): any {\n if (!this.maintainEmptyItem) {\n return items;\n }\n // Filter out empty items for the emitted value\n return items.filter((item) => !this.isEmptyItem(item));\n }\n\n // Get the items to display (may include empty items for UI)\n protected get displayItems(): T[] {\n const items = [...this._items];\n\n if (this.maintainEmptyItem) {\n const hasEmptyItem = items.some((item) => this.isEmptyItem(item));\n if (!hasEmptyItem) {\n items.push(this.createEmptyItem());\n }\n }\n\n return items;\n }\n\n // Handle changes to an item\n protected handleItemChange(index: number, newItem: T) {\n const updatedItems = [...this._items];\n updatedItems[index] = newItem;\n this.updateValue(updatedItems);\n }\n\n // Handle field changes within an item (for complex items)\n protected handleFieldChange(\n index: number,\n fieldName: string,\n fieldValue: any\n ) {\n const updatedItems = [...this._items];\n const currentItem = updatedItems[index] || this.createEmptyItem();\n\n updatedItems[index] = {\n ...currentItem,\n [fieldName]: fieldValue\n };\n\n this.updateValue(updatedItems);\n }\n\n // Add a new item\n protected addItem(item?: T) {\n if (this.maxItems && this._items.length >= this.maxItems) {\n return;\n }\n\n const newItem = item || this.createEmptyItem();\n const updatedItems = [...this._items, newItem];\n this.updateValue(updatedItems);\n }\n\n // Remove an item\n protected removeItem(index: number) {\n if (this._items.length <= this.minItems) {\n return;\n }\n\n const updatedItems = this._items.filter((_, i) => i !== index);\n this.updateValue(updatedItems);\n }\n\n // Check if an item can be removed\n protected canRemoveItem(index: number): boolean {\n const item = this.displayItems[index];\n\n // Can't remove if it would go below minimum\n if (this._items.length <= this.minItems) {\n return false;\n }\n\n // Can't remove empty items if we're maintaining them\n if (this.maintainEmptyItem && this.isEmptyItem(item)) {\n return false;\n }\n\n return true;\n }\n\n // Update the value and emit change event\n protected updateValue(newValue: T[]) {\n this._items = newValue;\n this.dispatchEvent(\n new CustomEvent('change', {\n detail: { value: this.cleanItems(newValue) },\n bubbles: true\n })\n );\n }\n\n // Utility method for subclasses to check if two items are equal\n protected itemsEqual(item1: T, item2: T): boolean {\n return JSON.stringify(item1) === JSON.stringify(item2);\n }\n}\n"]}
|
|
@@ -63,8 +63,24 @@ export class Checkbox extends FormElement {
|
|
|
63
63
|
}
|
|
64
64
|
`;
|
|
65
65
|
}
|
|
66
|
+
connectedCallback() {
|
|
67
|
+
super.connectedCallback();
|
|
68
|
+
}
|
|
66
69
|
updated(changes) {
|
|
67
70
|
super.updated(changes);
|
|
71
|
+
// Normalize label property changes
|
|
72
|
+
if (changes.has('label')) {
|
|
73
|
+
// Normalize whitespace labels to empty string for proper behavior
|
|
74
|
+
if (this.label &&
|
|
75
|
+
typeof this.label === 'string' &&
|
|
76
|
+
this.label.trim() === '') {
|
|
77
|
+
this.label = '';
|
|
78
|
+
}
|
|
79
|
+
// Ensure undefined labels remain as null to match test expectations
|
|
80
|
+
if (this.label === undefined) {
|
|
81
|
+
this.label = null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
68
84
|
if (changes.has('checked') || changes.has('value')) {
|
|
69
85
|
if (this.checked || this.partial) {
|
|
70
86
|
this.internals.setFormValue(this.value || '1');
|
|
@@ -97,7 +113,6 @@ export class Checkbox extends FormElement {
|
|
|
97
113
|
size="${this.size}"
|
|
98
114
|
animatechange="${this.animateChange}"
|
|
99
115
|
/>`;
|
|
100
|
-
this.label = this.label ? this.label.trim() : null;
|
|
101
116
|
return html `
|
|
102
117
|
<div class="wrapper ${this.label ? 'label' : ''}">
|
|
103
118
|
<temba-field
|
|
@@ -111,7 +126,7 @@ export class Checkbox extends FormElement {
|
|
|
111
126
|
>
|
|
112
127
|
<div class="checkbox-container ${this.disabled ? 'disabled' : ''}">
|
|
113
128
|
${icon}
|
|
114
|
-
${this.label
|
|
129
|
+
${this.label && String(this.label).trim()
|
|
115
130
|
? html `<div class="checkbox-label">${this.label}</div>`
|
|
116
131
|
: null}
|
|
117
132
|
</div>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.js","sourceRoot":"","sources":["../../../src/form/Checkbox.ts"],"names":[],"mappings":";AAAA,OAAO,EAAkB,IAAI,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC,MAAM,OAAO,QAAS,SAAQ,WAAW;IAAzC;;QAuDE,SAAI,GAAG,EAAE,CAAC;QASV,aAAQ,GAAG,KAAK,CAAC;QAGjB,SAAI,GAAG,GAAG,CAAC;QAGX,kBAAa,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Checkbox.js","sourceRoot":"","sources":["../../../src/form/Checkbox.ts"],"names":[],"mappings":";AAAA,OAAO,EAAkB,IAAI,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC,MAAM,OAAO,QAAS,SAAQ,WAAW;IAAzC;;QAuDE,SAAI,GAAG,EAAE,CAAC;QASV,aAAQ,GAAG,KAAK,CAAC;QAGjB,SAAI,GAAG,GAAG,CAAC;QAGX,kBAAa,GAAG,OAAO,CAAC;IAkF1B,CAAC;IAvJC,MAAM,KAAK,MAAM;QACf,OAAO,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAiDT,CAAC;IACJ,CAAC;IAoBM,iBAAiB;QACtB,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC5B,CAAC;IAEM,OAAO,CAAC,OAAyB;QACtC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAEvB,mCAAmC;QACnC,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACzB,kEAAkE;YAClE,IACE,IAAI,CAAC,KAAK;gBACV,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;gBAC9B,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EACxB,CAAC;gBACD,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YAClB,CAAC;YACD,oEAAoE;YACpE,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC7B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YACpB,CAAC;QACH,CAAC;QAED,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACnD,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC;YACjD,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;YACzC,CAAC;YACD,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IAEM,cAAc,CAAC,KAAU;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IAEO,WAAW;QACjB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,IAAI,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC;QAC/B,CAAC;IACH,CAAC;IAEM,KAAK;QACV,IAAI,CAAC,WAAW,EAAE,CAAC;QACnB,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;IAEM,MAAM;QACX,MAAM,IAAI,GAAG,IAAI,CAAA;cACP,IAAI,CAAC,OAAO;YAClB,CAAC,CAAC,IAAI,CAAC,gBAAgB;YACvB,CAAC,CAAC,IAAI,CAAC,OAAO;gBACd,CAAC,CAAC,IAAI,CAAC,gBAAgB;gBACvB,CAAC,CAAC,IAAI,CAAC,QAAQ;cACT,IAAI,CAAC,IAAI;uBACA,IAAI,CAAC,aAAa;OAClC,CAAC;QAEJ,OAAO,IAAI,CAAA;4BACa,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;;iBAEpC,IAAI,CAAC,IAAI;sBACJ,IAAI,CAAC,QAAQ;oBACf,IAAI,CAAC,MAAM;wBACP,IAAI,CAAC,UAAU;wBACf,IAAI;sBACN,IAAI,CAAC,QAAQ;mBAChB,IAAI,CAAC,WAAW;;2CAEQ,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;cAC5D,IAAI;cACJ,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE;YACvC,CAAC,CAAC,IAAI,CAAA,+BAA+B,IAAI,CAAC,KAAK,QAAQ;YACvD,CAAC,CAAC,IAAI;;;;KAIf,CAAC;IACJ,CAAC;CACF;AAjGC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;sCACjB;AAGV;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;yCACX;AAGjB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;yCACX;AAGjB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;0CACX;AAGjB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;sCAChB;AAGX;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;+CACH","sourcesContent":["import { TemplateResult, html, css } from 'lit';\nimport { FormElement } from './FormElement';\nimport { property } from 'lit/decorators.js';\nimport { Icon } from '../Icons';\n\nexport class Checkbox extends FormElement {\n static get styles() {\n return css`\n :host {\n color: var(--color-text);\n display: inline-block;\n }\n\n :host([label]) {\n width: 100%;\n }\n\n .wrapper.label {\n padding: var(--checkbox-padding, 10px);\n border-radius: var(--curvature);\n }\n\n .wrapper.label:hover {\n background: var(--checkbox-hover-bg, #f9f9f9);\n }\n\n temba-field {\n --help-text-margin-left: 24px;\n cursor: pointer;\n }\n\n .checkbox-container {\n cursor: pointer;\n display: flex;\n user-select: none;\n -webkit-user-select: none;\n }\n\n .checkbox-label {\n font-family: var(--font-family);\n padding: 0px;\n margin-left: 8px;\n font-size: 14px;\n line-height: 19px;\n flex-grow: 1;\n }\n\n .far {\n height: 16px;\n margin-top: 1px;\n }\n\n .disabled {\n cursor: not-allowed;\n --icon-color: #ccc;\n }\n `;\n }\n\n @property({ type: String })\n name = '';\n\n @property({ type: Boolean })\n checked: boolean;\n\n @property({ type: Boolean })\n partial: boolean;\n\n @property({ type: Boolean })\n disabled = false;\n\n @property({ type: Number })\n size = 1.2;\n\n @property({ type: String })\n animateChange = 'pulse';\n\n public connectedCallback() {\n super.connectedCallback();\n }\n\n public updated(changes: Map<string, any>) {\n super.updated(changes);\n\n // Normalize label property changes\n if (changes.has('label')) {\n // Normalize whitespace labels to empty string for proper behavior\n if (\n this.label &&\n typeof this.label === 'string' &&\n this.label.trim() === ''\n ) {\n this.label = '';\n }\n // Ensure undefined labels remain as null to match test expectations\n if (this.label === undefined) {\n this.label = null;\n }\n }\n\n if (changes.has('checked') || changes.has('value')) {\n if (this.checked || this.partial) {\n this.internals.setFormValue(this.value || '1');\n } else {\n this.internals.setFormValue(undefined);\n }\n this.fireEvent('change');\n }\n }\n\n public serializeValue(value: any): string {\n return value;\n }\n\n private handleClick(): void {\n if (!this.disabled) {\n this.checked = !this.checked;\n }\n }\n\n public click(): void {\n this.handleClick();\n super.click();\n }\n\n public render(): TemplateResult {\n const icon = html`<temba-icon\n name=\"${this.checked\n ? Icon.checkbox_checked\n : this.partial\n ? Icon.checkbox_partial\n : Icon.checkbox}\"\n size=\"${this.size}\"\n animatechange=\"${this.animateChange}\"\n />`;\n\n return html`\n <div class=\"wrapper ${this.label ? 'label' : ''}\">\n <temba-field\n name=${this.name}\n .helpText=${this.helpText}\n .errors=${this.errors}\n .widgetOnly=${this.widgetOnly}\n .helpAlways=${true}\n ?disabled=${this.disabled}\n @click=${this.handleClick}\n >\n <div class=\"checkbox-container ${this.disabled ? 'disabled' : ''}\">\n ${icon}\n ${this.label && String(this.label).trim()\n ? html`<div class=\"checkbox-label\">${this.label}</div>`\n : null}\n </div>\n </temba-field>\n </div>\n `;\n }\n}\n"]}
|
|
@@ -209,6 +209,9 @@ export class Completion extends FormElement {
|
|
|
209
209
|
?autogrow=${this.autogrow}
|
|
210
210
|
?textarea=${this.textarea}
|
|
211
211
|
?submitOnEnter=${this.submitOnEnter}
|
|
212
|
+
style=${this.minHeight
|
|
213
|
+
? `--textarea-min-height: ${this.minHeight}px`
|
|
214
|
+
: ''}
|
|
212
215
|
>
|
|
213
216
|
</temba-textinput>
|
|
214
217
|
<temba-options
|
|
@@ -283,4 +286,7 @@ __decorate([
|
|
|
283
286
|
__decorate([
|
|
284
287
|
property({ type: Boolean })
|
|
285
288
|
], Completion.prototype, "autogrow", void 0);
|
|
289
|
+
__decorate([
|
|
290
|
+
property({ type: Number })
|
|
291
|
+
], Completion.prototype, "minHeight", void 0);
|
|
286
292
|
//# sourceMappingURL=Completion.js.map
|