@nyaruka/temba-components 0.129.7 → 0.129.8
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 +8 -3
- 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 +42 -26
- package/dist/temba-components.js +506 -218
- 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/flow/NodeEditor.js +245 -22
- package/out-tsc/src/flow/NodeEditor.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/send_msg.js +147 -6
- package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
- package/out-tsc/src/flow/types.js.map +1 -1
- package/out-tsc/src/form/ArrayEditor.js +111 -38
- 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/FormField.js +1 -1
- 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 +12 -5
- package/out-tsc/src/form/TextInput.js.map +1 -1
- package/out-tsc/src/form/select/Select.js +4 -4
- package/out-tsc/src/form/select/Select.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +27 -2
- package/out-tsc/src/live/ContactChat.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/temba-field-config.test.js +4 -2
- package/out-tsc/test/temba-field-config.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 +71 -0
- package/out-tsc/test/temba-node-editor.test.js.map +1 -1
- package/out-tsc/test/temba-select.test.js +1 -1
- 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 +4 -0
- 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/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/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/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/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/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 +5 -0
- package/src/flow/NodeEditor.ts +269 -23
- package/src/flow/actions/call_webhook.ts +28 -18
- package/src/flow/actions/send_msg.ts +170 -6
- package/src/flow/types.ts +21 -2
- package/src/form/ArrayEditor.ts +120 -42
- package/src/form/BaseListEditor.ts +22 -6
- package/src/form/FormField.ts +1 -1
- 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 +15 -7
- package/src/form/select/Select.ts +4 -4
- package/src/live/ContactChat.ts +30 -4
- package/static/css/temba-components.css +2 -0
- package/static/mr/docs/en-us/editor.json +2588 -0
- package/stress-test.js +138 -0
- package/temba-modules.ts +2 -0
- package/test/temba-field-config.test.ts +4 -2
- package/test/temba-message-editor.test.ts +300 -0
- package/test/temba-node-editor.test.ts +94 -0
- package/test/temba-select.test.ts +1 -1
- package/test/temba-textinput.test.ts +26 -0
- package/test/temba-webchat.test.ts +5 -0
- package/test/utils.test.ts +2 -13
- package/test-assets/contacts/history.json +19 -0
- package/test-assets/style.css +2 -0
- package/web-dev-mock.mjs +433 -0
- package/web-dev-server.config.mjs +51 -5
- package/web-test-runner.config.mjs +9 -4
|
@@ -59,7 +59,8 @@ export class Thumbnail extends RapidElement {
|
|
|
59
59
|
background-repeat: no-repeat;
|
|
60
60
|
border-radius: var(--curvature);
|
|
61
61
|
max-height: calc(var(--thumb-size, 4em) * 2);
|
|
62
|
-
width: var(--thumb-size, 4em);
|
|
62
|
+
max-width: calc(var(--thumb-size, 4em) * 2);
|
|
63
|
+
height: var(--thumb-size, 4em);
|
|
63
64
|
display: flex;
|
|
64
65
|
align-items: center;
|
|
65
66
|
justify-content: center;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Thumbnail.js","sourceRoot":"","sources":["../../../src/display/Thumbnail.ts"],"names":[],"mappings":"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","sourcesContent":["import { PropertyValueMap, css, html } from 'lit';\nimport { RapidElement } from '../RapidElement';\nimport { property } from 'lit/decorators.js';\nimport { getClasses } from '../utils';\nimport { Lightbox } from './Lightbox';\nimport { WebChatIcon } from '../webchat';\n\nenum ThumbnailContentType {\n IMAGE = 'image',\n AUDIO = 'audio',\n VIDEO = 'video',\n DOCUMENT = 'document',\n OTHER = 'other'\n}\n\nconst ThumbnailIcons = {\n [ThumbnailContentType.IMAGE]: WebChatIcon.attachment_image,\n [ThumbnailContentType.AUDIO]: WebChatIcon.attachment_audio,\n [ThumbnailContentType.VIDEO]: WebChatIcon.attachment_video,\n [ThumbnailContentType.DOCUMENT]: WebChatIcon.attachment_document,\n [ThumbnailContentType.OTHER]: WebChatIcon.attachment\n};\n\nexport class Thumbnail extends RapidElement {\n static get styles() {\n return css`\n :host {\n display: inline;\n }\n\n .wrapper {\n padding: var(--thumb-padding, 0.4em);\n background: var(--thumb-background, #fff);\n box-shadow: var(--widget-box-shadow);\n cursor: pointer;\n border-radius: calc(var(--curvature) * 1.5);\n border: 0px solid #f3f3f3;\n }\n\n .wrapper.zoom {\n border: none;\n padding: 0 !important;\n border-radius: 0 !important;\n overflow: hidden !important;\n }\n\n .zoom .thumb {\n border-radius: 0px !important;\n width: calc(var(--thumb-size, 4em) + 0.8em);\n max-height: calc(90vh - 10em);\n }\n\n .thumb {\n background-size: cover;\n background-position: center;\n background-repeat: no-repeat;\n border-radius: var(--curvature);\n max-height: calc(var(--thumb-size, 4em) * 2);\n width: var(--thumb-size, 4em);\n display: flex;\n align-items: center;\n justify-content: center;\n font-weight: 400;\n color: var(--thumb-icon, #bbb);\n }\n\n .thumb.document,\n .thumb.audio,\n .thumb.video {\n border: 1px solid #eee;\n }\n\n .wrapper:hover .thumb.icon {\n }\n\n .viewer {\n display: block;\n }\n\n .zoom .viewer {\n display: block;\n }\n\n .download {\n display: none;\n position: absolute;\n right: 0em;\n bottom: 0em;\n border-radius: var(--curvature);\n transform: scale(0.2) translate(3em, 3em);\n padding: 0.4em;\n }\n\n .zoom .download {\n display: block;\n background: rgba(0, 0, 0, 0.5);\n }\n\n .zoom .download:hover {\n background: rgba(0, 0, 0, 0.6);\n cursor: pointer;\n }\n `;\n }\n\n @property({ type: String })\n url: string;\n\n @property({ type: String })\n attachment: string;\n\n @property({ type: Number })\n ratio: number = 0;\n\n @property({ type: Boolean })\n preview: boolean = true;\n\n @property({ type: Boolean, attribute: false })\n zoom: boolean;\n\n @property({ type: String, attribute: true })\n contentType: string;\n\n protected updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n\n if (\n changes.has('contentType') &&\n this.contentType === ThumbnailContentType.IMAGE\n ) {\n const toObserve = this.shadowRoot.querySelector('.observe');\n if (toObserve) {\n new ResizeObserver((e, observer) => {\n if (toObserve.clientHeight > 0 && toObserve.clientWidth > 0) {\n this.ratio = toObserve.clientHeight / toObserve.clientWidth;\n this.preview =\n this.ratio === 0 || (this.ratio > 0.25 && this.ratio <= 1.5);\n observer.disconnect();\n }\n }).observe(toObserve);\n }\n }\n\n // convert our attachment to a url and label\n if (changes.has('attachment')) {\n if (this.attachment) {\n const splitIndex = this.attachment.indexOf(':');\n if (splitIndex === -1) {\n this.url = this.attachment;\n } else {\n const contentType = this.attachment.substring(0, splitIndex);\n this.url = this.attachment.substring(splitIndex + 1);\n\n if (contentType.startsWith('image')) {\n this.contentType = ThumbnailContentType.IMAGE;\n } else if (contentType.startsWith('audio')) {\n this.contentType = ThumbnailContentType.AUDIO;\n } else if (contentType.startsWith('video')) {\n this.contentType = ThumbnailContentType.VIDEO;\n } else if (contentType.startsWith('application')) {\n this.contentType = ThumbnailContentType.DOCUMENT;\n } else {\n this.contentType = ThumbnailContentType.OTHER;\n }\n }\n }\n }\n }\n\n public handleThumbnailClicked() {\n if (this.contentType === ThumbnailContentType.IMAGE && this.preview) {\n window.setTimeout(() => {\n const lightbox = document.querySelector('temba-lightbox') as Lightbox;\n lightbox.showElement(this);\n }, 100);\n } else {\n window.open(this.url, '_blank');\n }\n }\n\n public handleDownload(e: Event) {\n e.stopPropagation();\n e.preventDefault();\n\n // open this.url in another tab\n window.open(this.url, '_blank');\n }\n\n public render() {\n return html`\n <div\n @click=${this.handleThumbnailClicked.bind(this)}\n class=\"${getClasses({ wrapper: true, zoom: this.zoom })}\"\n url=${this.url}\n >\n ${this.contentType === ThumbnailContentType.IMAGE && this.preview\n ? html`<div class=\"\"><div class=\"download\" @click=${this.handleDownload.bind(\n this\n )}><temba-icon size=\"1\" style=\"color:#fff;\" name=\"download\"></temba-icon></div><img\n class=\"observe thumb ${this.contentType}\"\n src=\"${this.url}\"\n ></img></div>`\n : html`<div\n style=\"padding:1em; background:rgba(0,0,0,.05);border-radius:var(--curvature);\"\n >\n <temba-icon\n size=\"1.5\"\n name=\"${ThumbnailIcons[this.contentType]}\"\n ></temba-icon>\n </div>`}\n </div>\n `;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"Thumbnail.js","sourceRoot":"","sources":["../../../src/display/Thumbnail.ts"],"names":[],"mappings":"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","sourcesContent":["import { PropertyValueMap, css, html } from 'lit';\nimport { RapidElement } from '../RapidElement';\nimport { property } from 'lit/decorators.js';\nimport { getClasses } from '../utils';\nimport { Lightbox } from './Lightbox';\nimport { WebChatIcon } from '../webchat';\n\nenum ThumbnailContentType {\n IMAGE = 'image',\n AUDIO = 'audio',\n VIDEO = 'video',\n DOCUMENT = 'document',\n OTHER = 'other'\n}\n\nconst ThumbnailIcons = {\n [ThumbnailContentType.IMAGE]: WebChatIcon.attachment_image,\n [ThumbnailContentType.AUDIO]: WebChatIcon.attachment_audio,\n [ThumbnailContentType.VIDEO]: WebChatIcon.attachment_video,\n [ThumbnailContentType.DOCUMENT]: WebChatIcon.attachment_document,\n [ThumbnailContentType.OTHER]: WebChatIcon.attachment\n};\n\nexport class Thumbnail extends RapidElement {\n static get styles() {\n return css`\n :host {\n display: inline;\n }\n\n .wrapper {\n padding: var(--thumb-padding, 0.4em);\n background: var(--thumb-background, #fff);\n box-shadow: var(--widget-box-shadow);\n cursor: pointer;\n border-radius: calc(var(--curvature) * 1.5);\n border: 0px solid #f3f3f3;\n }\n\n .wrapper.zoom {\n border: none;\n padding: 0 !important;\n border-radius: 0 !important;\n overflow: hidden !important;\n }\n\n .zoom .thumb {\n border-radius: 0px !important;\n width: calc(var(--thumb-size, 4em) + 0.8em);\n max-height: calc(90vh - 10em);\n }\n\n .thumb {\n background-size: cover;\n background-position: center;\n background-repeat: no-repeat;\n border-radius: var(--curvature);\n max-height: calc(var(--thumb-size, 4em) * 2);\n max-width: calc(var(--thumb-size, 4em) * 2);\n height: var(--thumb-size, 4em);\n display: flex;\n align-items: center;\n justify-content: center;\n font-weight: 400;\n color: var(--thumb-icon, #bbb);\n }\n\n .thumb.document,\n .thumb.audio,\n .thumb.video {\n border: 1px solid #eee;\n }\n\n .wrapper:hover .thumb.icon {\n }\n\n .viewer {\n display: block;\n }\n\n .zoom .viewer {\n display: block;\n }\n\n .download {\n display: none;\n position: absolute;\n right: 0em;\n bottom: 0em;\n border-radius: var(--curvature);\n transform: scale(0.2) translate(3em, 3em);\n padding: 0.4em;\n }\n\n .zoom .download {\n display: block;\n background: rgba(0, 0, 0, 0.5);\n }\n\n .zoom .download:hover {\n background: rgba(0, 0, 0, 0.6);\n cursor: pointer;\n }\n `;\n }\n\n @property({ type: String })\n url: string;\n\n @property({ type: String })\n attachment: string;\n\n @property({ type: Number })\n ratio: number = 0;\n\n @property({ type: Boolean })\n preview: boolean = true;\n\n @property({ type: Boolean, attribute: false })\n zoom: boolean;\n\n @property({ type: String, attribute: true })\n contentType: string;\n\n protected updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n\n if (\n changes.has('contentType') &&\n this.contentType === ThumbnailContentType.IMAGE\n ) {\n const toObserve = this.shadowRoot.querySelector('.observe');\n if (toObserve) {\n new ResizeObserver((e, observer) => {\n if (toObserve.clientHeight > 0 && toObserve.clientWidth > 0) {\n this.ratio = toObserve.clientHeight / toObserve.clientWidth;\n this.preview =\n this.ratio === 0 || (this.ratio > 0.25 && this.ratio <= 1.5);\n observer.disconnect();\n }\n }).observe(toObserve);\n }\n }\n\n // convert our attachment to a url and label\n if (changes.has('attachment')) {\n if (this.attachment) {\n const splitIndex = this.attachment.indexOf(':');\n if (splitIndex === -1) {\n this.url = this.attachment;\n } else {\n const contentType = this.attachment.substring(0, splitIndex);\n this.url = this.attachment.substring(splitIndex + 1);\n\n if (contentType.startsWith('image')) {\n this.contentType = ThumbnailContentType.IMAGE;\n } else if (contentType.startsWith('audio')) {\n this.contentType = ThumbnailContentType.AUDIO;\n } else if (contentType.startsWith('video')) {\n this.contentType = ThumbnailContentType.VIDEO;\n } else if (contentType.startsWith('application')) {\n this.contentType = ThumbnailContentType.DOCUMENT;\n } else {\n this.contentType = ThumbnailContentType.OTHER;\n }\n }\n }\n }\n }\n\n public handleThumbnailClicked() {\n if (this.contentType === ThumbnailContentType.IMAGE && this.preview) {\n window.setTimeout(() => {\n const lightbox = document.querySelector('temba-lightbox') as Lightbox;\n lightbox.showElement(this);\n }, 100);\n } else {\n window.open(this.url, '_blank');\n }\n }\n\n public handleDownload(e: Event) {\n e.stopPropagation();\n e.preventDefault();\n\n // open this.url in another tab\n window.open(this.url, '_blank');\n }\n\n public render() {\n return html`\n <div\n @click=${this.handleThumbnailClicked.bind(this)}\n class=\"${getClasses({ wrapper: true, zoom: this.zoom })}\"\n url=${this.url}\n >\n ${this.contentType === ThumbnailContentType.IMAGE && this.preview\n ? html`<div class=\"\"><div class=\"download\" @click=${this.handleDownload.bind(\n this\n )}><temba-icon size=\"1\" style=\"color:#fff;\" name=\"download\"></temba-icon></div><img\n class=\"observe thumb ${this.contentType}\"\n src=\"${this.url}\"\n ></img></div>`\n : html`<div\n style=\"padding:1em; background:rgba(0,0,0,.05);border-radius:var(--curvature);\"\n >\n <temba-icon\n size=\"1.5\"\n name=\"${ThumbnailIcons[this.contentType]}\"\n ></temba-icon>\n </div>`}\n </div>\n `;\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/events.ts"],"names":[],"mappings":"","sourcesContent":["import { Msg, ObjectReference, User } from './interfaces';\n\nexport interface EventGroup {\n type: string;\n events: ContactEvent[];\n open: boolean;\n}\n\nexport interface ContactEvent {\n uuid?: string;\n type: string;\n created_on: string;\n created_by?: User;\n}\n\nexport interface ChannelEvent extends ContactEvent {\n channel_event_type: string;\n duration: number;\n\n event: {\n type: string;\n channel: { uuid: string; name: string };\n duration?: number;\n optin?: {\n uuid: string;\n name: string;\n };\n };\n}\n\nexport interface ContactLanguageChangedEvent extends ContactEvent {\n language: string;\n step_uuid: string;\n session_uuid: string;\n}\n\nexport interface OptinRequestedEvent extends ContactEvent {\n optin: {\n uuid: string;\n name: string;\n };\n}\n\nexport interface MsgEvent extends ContactEvent {\n msg: Msg;\n status: string;\n failed_reason?: string;\n failed_reason_display?: string;\n logs_url: string;\n recipient_count?: number;\n created_by?: User;\n optin?: ObjectReference;\n}\n\nexport interface FlowEvent extends ContactEvent {\n flow: ObjectReference;\n status: string;\n}\n\nexport interface URNsChangedEvent extends ContactEvent {\n urns: string[];\n}\n\nexport interface TicketEvent extends ContactEvent {\n note?: string;\n assignee?: User;\n ticket: {\n uuid: string;\n topic?: ObjectReference;\n closed_on?: string;\n opened_on?: string;\n };\n topic?: ObjectReference;\n created_by?: User;\n}\n\nexport interface NameChangedEvent extends ContactEvent {\n name: string;\n}\n\nexport interface UpdateFieldEvent extends ContactEvent {\n field: { key: string; name: string };\n value: { text: string };\n}\n\nexport interface ContactGroupsEvent extends ContactEvent {\n groups_added: ObjectReference[];\n groups_removed: ObjectReference[];\n}\n\nexport interface AirtimeTransferredEvent extends ContactEvent {\n sender: string;\n recipient: string;\n currency: string;\n amount: string;\n}\n\nexport type CallStartedEvent = ContactEvent;\n\nexport interface ContactHistoryPage {\n has_older: boolean;\n recent_only: boolean;\n next_before: number;\n next_after: number;\n start_date: Date;\n events: ContactEvent[];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/events.ts"],"names":[],"mappings":"","sourcesContent":["import { Msg, ObjectReference, User } from './interfaces';\n\nexport interface EventGroup {\n type: string;\n events: ContactEvent[];\n open: boolean;\n}\n\nexport interface ContactEvent {\n uuid?: string;\n type: string;\n created_on: string;\n created_by?: User;\n}\n\nexport interface ChannelEvent extends ContactEvent {\n channel_event_type: string;\n duration: number;\n\n event: {\n type: string;\n channel: { uuid: string; name: string };\n duration?: number;\n optin?: {\n uuid: string;\n name: string;\n };\n };\n}\n\nexport interface ContactLanguageChangedEvent extends ContactEvent {\n language: string;\n step_uuid: string;\n session_uuid: string;\n}\n\nexport interface OptinRequestedEvent extends ContactEvent {\n optin: {\n uuid: string;\n name: string;\n };\n}\n\nexport interface MsgEvent extends ContactEvent {\n msg: Msg;\n status: string;\n failed_reason?: string;\n failed_reason_display?: string;\n logs_url: string;\n recipient_count?: number;\n created_by?: User;\n optin?: ObjectReference;\n}\n\nexport interface FlowEvent extends ContactEvent {\n flow: ObjectReference;\n status: string;\n}\n\nexport interface RunEvent extends ContactEvent {\n flow: ObjectReference;\n status: string;\n}\n\nexport interface URNsChangedEvent extends ContactEvent {\n urns: string[];\n}\n\nexport interface TicketEvent extends ContactEvent {\n note?: string;\n assignee?: User;\n ticket: {\n uuid: string;\n topic?: ObjectReference;\n closed_on?: string;\n opened_on?: string;\n };\n topic?: ObjectReference;\n created_by?: User;\n}\n\nexport interface NameChangedEvent extends ContactEvent {\n name: string;\n}\n\nexport interface UpdateFieldEvent extends ContactEvent {\n field: { key: string; name: string };\n value: { text: string };\n}\n\nexport interface ContactGroupsEvent extends ContactEvent {\n groups_added: ObjectReference[];\n groups_removed: ObjectReference[];\n}\n\nexport interface AirtimeTransferredEvent extends ContactEvent {\n sender: string;\n recipient: string;\n currency: string;\n amount: string;\n}\n\nexport type CallStartedEvent = ContactEvent;\n\nexport interface ContactHistoryPage {\n has_older: boolean;\n recent_only: boolean;\n next_before: number;\n next_after: number;\n start_date: Date;\n events: ContactEvent[];\n}\n"]}
|
|
@@ -13,6 +13,7 @@ export class NodeEditor extends RapidElement {
|
|
|
13
13
|
this.originalFormData = {};
|
|
14
14
|
this.errors = {};
|
|
15
15
|
this.groupCollapseState = {};
|
|
16
|
+
this.groupHoverState = {};
|
|
16
17
|
}
|
|
17
18
|
static get styles() {
|
|
18
19
|
return css `
|
|
@@ -23,6 +24,10 @@ export class NodeEditor extends RapidElement {
|
|
|
23
24
|
gap: 15px;
|
|
24
25
|
min-width: 400px;
|
|
25
26
|
padding-bottom: 40px;
|
|
27
|
+
|
|
28
|
+
--color-bubble-bg: rgba(255, 255, 255, 0.8);
|
|
29
|
+
--color-bubble-border: #999;
|
|
30
|
+
--color-bubble-text: #777;
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
.form-field {
|
|
@@ -31,10 +36,6 @@ export class NodeEditor extends RapidElement {
|
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
.form-field label {
|
|
34
|
-
font-weight: 500;
|
|
35
|
-
margin-bottom: 6px;
|
|
36
|
-
color: #333;
|
|
37
|
-
font-size: 14px;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
.field-errors {
|
|
@@ -95,10 +96,16 @@ export class NodeEditor extends RapidElement {
|
|
|
95
96
|
border-color: var(--color-error, tomato);
|
|
96
97
|
}
|
|
97
98
|
|
|
99
|
+
.form-group.has-bubble {
|
|
100
|
+
border-width: 1px;
|
|
101
|
+
border-color: var(--color-bubble-border, #aaa);
|
|
102
|
+
}
|
|
103
|
+
|
|
98
104
|
.form-group-header {
|
|
99
105
|
background: #f8f9fa;
|
|
100
|
-
padding:
|
|
106
|
+
padding: 8px 10px;
|
|
101
107
|
border-bottom: 1px solid #e0e0e0;
|
|
108
|
+
|
|
102
109
|
display: flex;
|
|
103
110
|
align-items: center;
|
|
104
111
|
justify-content: space-between;
|
|
@@ -106,6 +113,17 @@ export class NodeEditor extends RapidElement {
|
|
|
106
113
|
user-select: none;
|
|
107
114
|
}
|
|
108
115
|
|
|
116
|
+
.form-group.has-bubble .form-group-header {
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.collapsed .form-group-header {
|
|
120
|
+
border: none;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.form-group-header:hover {
|
|
124
|
+
background: rgba(0, 0, 0, 0.05);
|
|
125
|
+
}
|
|
126
|
+
|
|
109
127
|
.form-group-header.collapsible:hover {
|
|
110
128
|
background: #f1f3f4;
|
|
111
129
|
}
|
|
@@ -116,7 +134,7 @@ export class NodeEditor extends RapidElement {
|
|
|
116
134
|
|
|
117
135
|
.form-group-title {
|
|
118
136
|
font-weight: 500;
|
|
119
|
-
color: #
|
|
137
|
+
color: var(--color-label, #777);
|
|
120
138
|
font-size: 14px;
|
|
121
139
|
display: flex;
|
|
122
140
|
}
|
|
@@ -139,13 +157,13 @@ export class NodeEditor extends RapidElement {
|
|
|
139
157
|
}
|
|
140
158
|
|
|
141
159
|
.form-group-content {
|
|
142
|
-
padding:
|
|
160
|
+
padding: 6px;
|
|
143
161
|
display: flex;
|
|
144
162
|
flex-direction: column;
|
|
145
163
|
gap: 15px;
|
|
146
164
|
overflow: hidden;
|
|
147
|
-
transition: all 0.
|
|
148
|
-
|
|
165
|
+
transition: all 0.2s ease-in-out;
|
|
166
|
+
|
|
149
167
|
opacity: 1;
|
|
150
168
|
}
|
|
151
169
|
|
|
@@ -158,9 +176,14 @@ export class NodeEditor extends RapidElement {
|
|
|
158
176
|
|
|
159
177
|
.group-toggle-icon {
|
|
160
178
|
color: #666;
|
|
161
|
-
transition: transform 0.3s ease;
|
|
179
|
+
transition: transform 0.3s ease, opacity 0.3s ease;
|
|
162
180
|
cursor: pointer;
|
|
163
181
|
transform: rotate(0deg);
|
|
182
|
+
opacity: 1;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.group-toggle-icon.faded {
|
|
186
|
+
opacity: 0;
|
|
164
187
|
}
|
|
165
188
|
|
|
166
189
|
.group-toggle-icon.expanded {
|
|
@@ -179,6 +202,58 @@ export class NodeEditor extends RapidElement {
|
|
|
179
202
|
color: var(--color-error, tomato);
|
|
180
203
|
margin-right: 8px;
|
|
181
204
|
}
|
|
205
|
+
|
|
206
|
+
.group-count-bubble {
|
|
207
|
+
border-radius: 50%;
|
|
208
|
+
display: flex;
|
|
209
|
+
align-items: center;
|
|
210
|
+
justify-content: center;
|
|
211
|
+
font-size: 11px;
|
|
212
|
+
font-weight: 600;
|
|
213
|
+
padding: 4px;
|
|
214
|
+
min-width: 12px;
|
|
215
|
+
min-height: 12px;
|
|
216
|
+
position: absolute;
|
|
217
|
+
top: 50%;
|
|
218
|
+
left: 50%;
|
|
219
|
+
transform: translate(-50%, -50%);
|
|
220
|
+
line-height: 0px;
|
|
221
|
+
opacity: 1;
|
|
222
|
+
transition: opacity 0.3s ease;
|
|
223
|
+
background: var(--color-bubble-bg, #fff);
|
|
224
|
+
border: 1px solid var(--color-bubble-border, #777);
|
|
225
|
+
color: var(--color-bubble-text, #000);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.group-count-bubble.hidden {
|
|
229
|
+
opacity: 0;
|
|
230
|
+
pointer-events: none;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.group-checkmark-icon {
|
|
234
|
+
position: absolute;
|
|
235
|
+
top: 50%;
|
|
236
|
+
left: 50%;
|
|
237
|
+
transform: translate(-50%, -50%);
|
|
238
|
+
opacity: 1;
|
|
239
|
+
transition: opacity 0.3s ease;
|
|
240
|
+
border-radius: 50%;
|
|
241
|
+
color: var(--color-bubble-text, #000);
|
|
242
|
+
background: var(--color-bubble-bg, #fff);
|
|
243
|
+
border: 1px solid var(--color-bubble-border, #777);
|
|
244
|
+
padding: 0.2em;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.group-checkmark-icon.hidden {
|
|
248
|
+
opacity: 0;
|
|
249
|
+
pointer-events: none;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.group-toggle-container {
|
|
253
|
+
position: relative;
|
|
254
|
+
display: flex;
|
|
255
|
+
align-items: center;
|
|
256
|
+
}
|
|
182
257
|
`;
|
|
183
258
|
}
|
|
184
259
|
connectedCallback() {
|
|
@@ -206,6 +281,7 @@ export class NodeEditor extends RapidElement {
|
|
|
206
281
|
this.formData = {};
|
|
207
282
|
this.errors = {};
|
|
208
283
|
this.groupCollapseState = {};
|
|
284
|
+
this.groupHoverState = {};
|
|
209
285
|
}
|
|
210
286
|
initializeFormData() {
|
|
211
287
|
if (this.action) {
|
|
@@ -417,7 +493,7 @@ export class NodeEditor extends RapidElement {
|
|
|
417
493
|
// Check required fields
|
|
418
494
|
if (fieldConfig.required &&
|
|
419
495
|
(!value || (Array.isArray(value) && value.length === 0))) {
|
|
420
|
-
errors[fieldName] = `${fieldConfig.label || fieldName} is required
|
|
496
|
+
errors[fieldName] = `${fieldConfig.label || fieldName} is required.`;
|
|
421
497
|
}
|
|
422
498
|
// Check minLength for text fields
|
|
423
499
|
if (typeof value === 'string' &&
|
|
@@ -437,6 +513,9 @@ export class NodeEditor extends RapidElement {
|
|
|
437
513
|
if (actionConfig === null || actionConfig === void 0 ? void 0 : actionConfig.validate) {
|
|
438
514
|
// Convert form data back to action for validation
|
|
439
515
|
let actionForValidation;
|
|
516
|
+
if (actionConfig.sanitize) {
|
|
517
|
+
actionConfig.sanitize(this.formData);
|
|
518
|
+
}
|
|
440
519
|
if (actionConfig.fromFormData) {
|
|
441
520
|
actionForValidation = actionConfig.fromFormData(this.formData);
|
|
442
521
|
}
|
|
@@ -693,9 +772,43 @@ export class NodeEditor extends RapidElement {
|
|
|
693
772
|
}
|
|
694
773
|
// Check for computed values in dependent fields
|
|
695
774
|
this.updateComputedFields(propertyName);
|
|
775
|
+
// Re-evaluate group collapse states that depend on form data
|
|
776
|
+
this.updateGroupCollapseStates();
|
|
696
777
|
// Trigger re-render to handle conditional field visibility
|
|
697
778
|
this.requestUpdate();
|
|
698
779
|
}
|
|
780
|
+
updateGroupCollapseStates() {
|
|
781
|
+
if (!this.action)
|
|
782
|
+
return;
|
|
783
|
+
const config = ACTION_CONFIG[this.action.type];
|
|
784
|
+
if (!(config === null || config === void 0 ? void 0 : config.layout))
|
|
785
|
+
return;
|
|
786
|
+
this.updateGroupCollapseStatesRecursive(config.layout);
|
|
787
|
+
}
|
|
788
|
+
updateGroupCollapseStatesRecursive(items) {
|
|
789
|
+
items.forEach((item) => {
|
|
790
|
+
if (typeof item === 'object' && item.type === 'group') {
|
|
791
|
+
const { label, collapsed, collapsible } = item;
|
|
792
|
+
// Only update if the group is collapsible and has a function-based collapsed property
|
|
793
|
+
if (collapsible && typeof collapsed === 'function') {
|
|
794
|
+
const newCollapsedState = collapsed(this.formData);
|
|
795
|
+
// Only update if the state has changed to avoid unnecessary re-renders
|
|
796
|
+
if (this.groupCollapseState[label] !== newCollapsedState) {
|
|
797
|
+
this.groupCollapseState = {
|
|
798
|
+
...this.groupCollapseState,
|
|
799
|
+
[label]: newCollapsedState
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
// Recursively check nested items
|
|
804
|
+
this.updateGroupCollapseStatesRecursive(item.items);
|
|
805
|
+
}
|
|
806
|
+
else if (typeof item === 'object' && item.type === 'row') {
|
|
807
|
+
// Recursively check items in rows
|
|
808
|
+
this.updateGroupCollapseStatesRecursive(item.items);
|
|
809
|
+
}
|
|
810
|
+
});
|
|
811
|
+
}
|
|
699
812
|
updateComputedFields(changedFieldName) {
|
|
700
813
|
if (!this.action)
|
|
701
814
|
return;
|
|
@@ -813,6 +926,7 @@ export class NodeEditor extends RapidElement {
|
|
|
813
926
|
nameKey="${selectConfig.nameKey || 'name'}"
|
|
814
927
|
endpoint="${selectConfig.endpoint || ''}"
|
|
815
928
|
.helpText="${config.helpText || ''}"
|
|
929
|
+
flavor="${selectConfig.flavor || 'small'}"
|
|
816
930
|
@change="${(e) => this.handleFormFieldChange(fieldName, e)}"
|
|
817
931
|
>
|
|
818
932
|
${(_a = selectConfig.options) === null || _a === void 0 ? void 0 : _a.map((option) => {
|
|
@@ -860,8 +974,10 @@ export class NodeEditor extends RapidElement {
|
|
|
860
974
|
.sortable="${config.sortable}"
|
|
861
975
|
.itemLabel="${config.itemLabel || 'Item'}"
|
|
862
976
|
.minItems="${config.minItems || 0}"
|
|
977
|
+
.maxItems="${config.maxItems || 0}"
|
|
863
978
|
.onItemChange="${config.onItemChange}"
|
|
864
|
-
|
|
979
|
+
.isEmptyItemFn="${config.isEmptyItem}"
|
|
980
|
+
@change="${(e) => this.handleNewFieldChange(fieldName, e.target.value)}"
|
|
865
981
|
></temba-array-editor>
|
|
866
982
|
${errors.length
|
|
867
983
|
? html `<div class="field-errors">${errors.join(', ')}</div>`
|
|
@@ -885,6 +1001,28 @@ export class NodeEditor extends RapidElement {
|
|
|
885
1001
|
? html `<div class="field-errors">${errors.join(', ')}</div>`
|
|
886
1002
|
: ''}
|
|
887
1003
|
</div>`;
|
|
1004
|
+
}
|
|
1005
|
+
case 'message-editor': {
|
|
1006
|
+
const messageConfig = config;
|
|
1007
|
+
return html `<temba-message-editor
|
|
1008
|
+
name="${fieldName}"
|
|
1009
|
+
label="${config.label}"
|
|
1010
|
+
?required="${config.required}"
|
|
1011
|
+
.errors="${errors}"
|
|
1012
|
+
.value="${value || ''}"
|
|
1013
|
+
.attachments="${this.formData.attachments || []}"
|
|
1014
|
+
placeholder="${messageConfig.placeholder || ''}"
|
|
1015
|
+
.helpText="${config.helpText || ''}"
|
|
1016
|
+
?autogrow="${messageConfig.autogrow}"
|
|
1017
|
+
?gsm="${messageConfig.gsm}"
|
|
1018
|
+
?disableCompletion="${messageConfig.disableCompletion}"
|
|
1019
|
+
counter="${messageConfig.counter || ''}"
|
|
1020
|
+
accept="${messageConfig.accept || ''}"
|
|
1021
|
+
endpoint="${messageConfig.endpoint || ''}"
|
|
1022
|
+
max-attachments="${messageConfig.maxAttachments || 3}"
|
|
1023
|
+
minHeight="${messageConfig.minHeight || 60}"
|
|
1024
|
+
@change="${(e) => this.handleMessageEditorChange(fieldName, e)}"
|
|
1025
|
+
></temba-message-editor>`;
|
|
888
1026
|
}
|
|
889
1027
|
default:
|
|
890
1028
|
return html `<div>Unsupported field type: ${config.type}</div>`;
|
|
@@ -896,6 +1034,18 @@ export class NodeEditor extends RapidElement {
|
|
|
896
1034
|
[groupLabel]: !this.groupCollapseState[groupLabel]
|
|
897
1035
|
};
|
|
898
1036
|
}
|
|
1037
|
+
handleGroupMouseEnter(groupLabel) {
|
|
1038
|
+
this.groupHoverState = {
|
|
1039
|
+
...this.groupHoverState,
|
|
1040
|
+
[groupLabel]: true
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
handleGroupMouseLeave(groupLabel) {
|
|
1044
|
+
this.groupHoverState = {
|
|
1045
|
+
...this.groupHoverState,
|
|
1046
|
+
[groupLabel]: false
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
899
1049
|
expandGroupsWithErrors(errors) {
|
|
900
1050
|
if (!this.action)
|
|
901
1051
|
return;
|
|
@@ -969,25 +1119,54 @@ export class NodeEditor extends RapidElement {
|
|
|
969
1119
|
`;
|
|
970
1120
|
}
|
|
971
1121
|
renderGroup(groupConfig, config, renderedFields) {
|
|
972
|
-
var _a;
|
|
973
|
-
const { label, items, collapsible = false, collapsed = false, helpText } = groupConfig;
|
|
1122
|
+
var _a, _b;
|
|
1123
|
+
const { label, items, collapsible = false, collapsed = false, helpText, getGroupValueCount } = groupConfig;
|
|
974
1124
|
// Initialize collapse state if not set
|
|
975
1125
|
if (collapsible && !(label in this.groupCollapseState)) {
|
|
1126
|
+
// Evaluate collapsed property - can be boolean or function
|
|
1127
|
+
const initialCollapsed = typeof collapsed === 'function' ? collapsed(this.formData) : collapsed;
|
|
976
1128
|
this.groupCollapseState = {
|
|
977
1129
|
...this.groupCollapseState,
|
|
978
|
-
[label]:
|
|
1130
|
+
[label]: initialCollapsed
|
|
979
1131
|
};
|
|
980
1132
|
}
|
|
981
1133
|
const isCollapsed = collapsible
|
|
982
|
-
? (_a = this.groupCollapseState[label]) !== null && _a !== void 0 ? _a : collapsed
|
|
1134
|
+
? (_a = this.groupCollapseState[label]) !== null && _a !== void 0 ? _a : (typeof collapsed === 'function' ? collapsed(this.formData) : collapsed)
|
|
983
1135
|
: false;
|
|
984
1136
|
// Check if any field in this group has errors
|
|
985
1137
|
const fieldsInGroup = this.collectFieldsFromItems(items);
|
|
986
1138
|
const groupHasErrors = fieldsInGroup.some((fieldName) => this.errors[fieldName]);
|
|
1139
|
+
// Calculate count for bubble display
|
|
1140
|
+
let valueCount = 0;
|
|
1141
|
+
let showBubble = false;
|
|
1142
|
+
let showCheckmark = false;
|
|
1143
|
+
let hasValue = false;
|
|
1144
|
+
const isHovered = (_b = this.groupHoverState[label]) !== null && _b !== void 0 ? _b : false;
|
|
1145
|
+
if (getGroupValueCount && collapsible) {
|
|
1146
|
+
try {
|
|
1147
|
+
const result = getGroupValueCount(this.formData);
|
|
1148
|
+
if (typeof result === 'boolean') {
|
|
1149
|
+
// Boolean result - show checkmark when true
|
|
1150
|
+
showCheckmark = result && isCollapsed && !isHovered;
|
|
1151
|
+
hasValue = result;
|
|
1152
|
+
}
|
|
1153
|
+
else if (typeof result === 'number') {
|
|
1154
|
+
// Numeric result - show count bubble
|
|
1155
|
+
valueCount = result;
|
|
1156
|
+
showBubble = valueCount > 0 && isCollapsed && !isHovered;
|
|
1157
|
+
hasValue = valueCount > 0;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
catch (error) {
|
|
1161
|
+
console.error(`Error calculating group value count for ${label}:`, error);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
987
1164
|
return html `
|
|
988
1165
|
<div
|
|
989
1166
|
class="form-group ${collapsible ? 'collapsible' : ''} ${groupHasErrors
|
|
990
1167
|
? 'has-errors'
|
|
1168
|
+
: ''} ${isCollapsed ? 'collapsed' : 'expanded'} ${hasValue
|
|
1169
|
+
? 'has-bubble'
|
|
991
1170
|
: ''}"
|
|
992
1171
|
>
|
|
993
1172
|
<div
|
|
@@ -995,6 +1174,12 @@ export class NodeEditor extends RapidElement {
|
|
|
995
1174
|
@click=${collapsible
|
|
996
1175
|
? () => this.handleGroupToggle(label)
|
|
997
1176
|
: undefined}
|
|
1177
|
+
@mouseenter=${collapsible
|
|
1178
|
+
? () => this.handleGroupMouseEnter(label)
|
|
1179
|
+
: undefined}
|
|
1180
|
+
@mouseleave=${collapsible
|
|
1181
|
+
? () => this.handleGroupMouseLeave(label)
|
|
1182
|
+
: undefined}
|
|
998
1183
|
>
|
|
999
1184
|
<div class="form-group-info">
|
|
1000
1185
|
<div class="form-group-title">${label}</div>
|
|
@@ -1010,13 +1195,28 @@ export class NodeEditor extends RapidElement {
|
|
|
1010
1195
|
></temba-icon>`
|
|
1011
1196
|
: ''}
|
|
1012
1197
|
${collapsible && !groupHasErrors
|
|
1013
|
-
? html `<
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1198
|
+
? html `<div class="group-toggle-container">
|
|
1199
|
+
<temba-icon
|
|
1200
|
+
name="arrow_right"
|
|
1201
|
+
size="1.5"
|
|
1202
|
+
class="group-toggle-icon ${isCollapsed
|
|
1017
1203
|
? 'collapsed'
|
|
1018
|
-
: 'expanded'}"
|
|
1019
|
-
|
|
1204
|
+
: 'expanded'} ${showBubble || showCheckmark ? 'faded' : ''}"
|
|
1205
|
+
></temba-icon>
|
|
1206
|
+
${showCheckmark
|
|
1207
|
+
? html `<temba-icon
|
|
1208
|
+
name="check"
|
|
1209
|
+
size="1"
|
|
1210
|
+
class="group-checkmark-icon"
|
|
1211
|
+
></temba-icon>`
|
|
1212
|
+
: showBubble
|
|
1213
|
+
? html `<div
|
|
1214
|
+
class="group-count-bubble ${!showBubble ? 'hidden' : ''}"
|
|
1215
|
+
>
|
|
1216
|
+
${valueCount}
|
|
1217
|
+
</div>`
|
|
1218
|
+
: ''}
|
|
1219
|
+
</div>`
|
|
1020
1220
|
: ''}
|
|
1021
1221
|
</div>
|
|
1022
1222
|
<div
|
|
@@ -1064,6 +1264,26 @@ export class NodeEditor extends RapidElement {
|
|
|
1064
1264
|
delete newErrors[fieldName];
|
|
1065
1265
|
this.errors = newErrors;
|
|
1066
1266
|
}
|
|
1267
|
+
// Re-evaluate group collapse states that depend on form data
|
|
1268
|
+
this.updateGroupCollapseStates();
|
|
1269
|
+
// Trigger re-render
|
|
1270
|
+
this.requestUpdate();
|
|
1271
|
+
}
|
|
1272
|
+
handleMessageEditorChange(fieldName, event) {
|
|
1273
|
+
const target = event.target;
|
|
1274
|
+
// Update both text and attachments from the message editor
|
|
1275
|
+
this.formData = {
|
|
1276
|
+
...this.formData,
|
|
1277
|
+
[fieldName]: target.value,
|
|
1278
|
+
attachments: target.attachments || []
|
|
1279
|
+
};
|
|
1280
|
+
// Clear any existing errors for both fields
|
|
1281
|
+
if (this.errors[fieldName]) {
|
|
1282
|
+
const newErrors = { ...this.errors };
|
|
1283
|
+
delete newErrors[fieldName];
|
|
1284
|
+
delete newErrors.attachments;
|
|
1285
|
+
this.errors = newErrors;
|
|
1286
|
+
}
|
|
1067
1287
|
// Trigger re-render
|
|
1068
1288
|
this.requestUpdate();
|
|
1069
1289
|
}
|
|
@@ -1208,4 +1428,7 @@ __decorate([
|
|
|
1208
1428
|
__decorate([
|
|
1209
1429
|
state()
|
|
1210
1430
|
], NodeEditor.prototype, "groupCollapseState", void 0);
|
|
1431
|
+
__decorate([
|
|
1432
|
+
state()
|
|
1433
|
+
], NodeEditor.prototype, "groupHoverState", void 0);
|
|
1211
1434
|
//# sourceMappingURL=NodeEditor.js.map
|