@nyaruka/temba-components 0.129.1 → 0.129.3
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/.github/workflows/build.yml +6 -5
- package/.github/workflows/coverage.yml +80 -0
- package/CHANGELOG.md +32 -0
- package/README.md +6 -0
- package/check-coverage.js +133 -0
- package/demo/components/alert/example.html +71 -0
- package/demo/components/button/example.html +167 -0
- package/demo/{chart → components/chart}/example.html +3 -3
- package/demo/components/chart/horizontal-demo.html +160 -0
- package/demo/components/checkbox/example.html +68 -0
- package/demo/components/compose/example.html +69 -0
- package/demo/components/datepicker/example.html +3 -3
- package/demo/components/datepicker/range-picker-demo.html +2 -2
- package/demo/{dialog → components/dialog}/example.html +3 -3
- package/demo/components/dropdown/example.html +95 -0
- package/demo/{flow → components/flow}/example.html +1 -1
- package/demo/{misc → components/misc}/example.html +3 -3
- package/demo/components/progress/example.html +62 -0
- package/demo/components/select/drag-and-drop.html +162 -0
- package/demo/components/select/example.html +76 -0
- package/demo/components/select/multi.html +72 -0
- package/demo/components/slider/example.html +55 -0
- package/demo/{sortable-list → components/sortable-list}/example.html +3 -3
- package/demo/components/tabs/example.html +91 -0
- package/demo/{textinput → components/textinput}/completion.html +3 -3
- package/demo/components/textinput/example.html +141 -0
- package/demo/{webchat → components/webchat}/example.html +3 -3
- package/demo/data/flows/sample-flow.json +107 -100
- package/demo/index.html +21 -21
- package/demo/static/css/styles.css +253 -0
- package/demo/sticky-note-demo.html +88 -85
- package/demo/styles.css +24 -0
- package/dist/locales/es.js +5 -5
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/fr.js +5 -5
- package/dist/locales/fr.js.map +1 -1
- package/dist/locales/locale-codes.js +2 -11
- package/dist/locales/locale-codes.js.map +1 -1
- package/dist/locales/pt.js +5 -5
- package/dist/locales/pt.js.map +1 -1
- package/dist/temba-components.js +893 -476
- package/dist/temba-components.js.map +1 -1
- package/generate-coverage-badge.sh +69 -0
- package/out-tsc/src/{vectoricon/index.js → Icons.js} +1 -1
- package/out-tsc/src/Icons.js.map +1 -0
- package/out-tsc/src/display/Alert.js.map +1 -0
- package/out-tsc/src/display/Anchor.js.map +1 -0
- package/out-tsc/src/display/Button.js.map +1 -0
- package/out-tsc/src/{charcount → display}/CharCount.js +159 -2
- package/out-tsc/src/display/CharCount.js.map +1 -0
- package/out-tsc/src/display/Chat.js.map +1 -0
- package/out-tsc/src/display/ContactName.js.map +1 -0
- package/out-tsc/src/display/ContactUrn.js.map +1 -0
- package/out-tsc/src/display/Dropdown.js.map +1 -0
- package/out-tsc/src/{vectoricon/VectorIcon.js → display/Icon.js} +2 -2
- package/out-tsc/src/display/Icon.js.map +1 -0
- package/out-tsc/src/display/Label.js.map +1 -0
- package/out-tsc/src/{leafletmap → display}/LeafletMap.js +16 -1
- package/out-tsc/src/display/LeafletMap.js.map +1 -0
- package/out-tsc/src/display/Lightbox.js.map +1 -0
- package/out-tsc/src/{loading → display}/Loading.js.map +1 -1
- package/out-tsc/src/{options → display}/Options.js.map +1 -1
- package/out-tsc/src/display/ProgressBar.js.map +1 -0
- package/out-tsc/src/display/TembaDate.js.map +1 -0
- package/out-tsc/src/display/TembaUser.js.map +1 -0
- package/out-tsc/src/display/Thumbnail.js.map +1 -0
- package/out-tsc/src/{tip → display}/Tip.js +1 -2
- package/out-tsc/src/display/Tip.js.map +1 -0
- package/out-tsc/src/display/Toast.js.map +1 -0
- package/out-tsc/src/display/sms/gsmsplitter.js.map +1 -0
- package/out-tsc/src/display/sms/gsmvalidator.js.map +1 -0
- package/out-tsc/src/display/sms/index.js.map +1 -0
- package/out-tsc/src/display/sms/unicodesplitter.js.map +1 -0
- package/out-tsc/src/events.js +2 -0
- package/out-tsc/src/events.js.map +1 -0
- package/out-tsc/src/excellent/ExcellentParser.js.map +1 -0
- package/out-tsc/src/excellent/helpers.js.map +1 -0
- package/out-tsc/src/flow/Editor.js +533 -140
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/EditorNode.js +287 -20
- package/out-tsc/src/flow/EditorNode.js.map +1 -1
- package/out-tsc/src/flow/Plumber.js +154 -74
- package/out-tsc/src/flow/Plumber.js.map +1 -1
- package/out-tsc/src/flow/StickyNote.js +153 -9
- package/out-tsc/src/flow/StickyNote.js.map +1 -1
- package/out-tsc/src/flow/config.js +88 -18
- package/out-tsc/src/flow/config.js.map +1 -1
- package/out-tsc/src/flow/render.js +327 -10
- package/out-tsc/src/flow/render.js.map +1 -1
- package/out-tsc/src/{checkbox → form}/Checkbox.js +2 -2
- package/out-tsc/src/form/Checkbox.js.map +1 -0
- package/out-tsc/src/{colorpicker → form}/ColorPicker.js +1 -1
- package/out-tsc/src/form/ColorPicker.js.map +1 -0
- package/out-tsc/src/{completion → form}/Completion.js +2 -2
- package/out-tsc/src/form/Completion.js.map +1 -0
- package/out-tsc/src/{compose → form}/Compose.js +1 -1
- package/out-tsc/src/form/Compose.js.map +1 -0
- package/out-tsc/src/{contactsearch → form}/ContactSearch.js +2 -2
- package/out-tsc/src/form/ContactSearch.js.map +1 -0
- package/out-tsc/src/form/CroppieCSS.js.map +1 -0
- package/out-tsc/src/{datepicker → form}/DatePicker.js +1 -1
- package/out-tsc/src/form/DatePicker.js.map +1 -0
- package/out-tsc/src/{FormElement.js → form/FormElement.js} +1 -1
- package/out-tsc/src/form/FormElement.js.map +1 -0
- package/out-tsc/src/form/FormField.js.map +1 -0
- package/out-tsc/src/{imagepicker → form}/ImagePicker.js +2 -2
- package/out-tsc/src/form/ImagePicker.js.map +1 -0
- package/out-tsc/src/{mediapicker → form}/MediaPicker.js +1 -1
- package/out-tsc/src/form/MediaPicker.js.map +1 -0
- package/out-tsc/src/{datepicker → form}/RangePicker.js +3 -2
- package/out-tsc/src/form/RangePicker.js.map +1 -0
- package/out-tsc/src/{slider → form}/TembaSlider.js +1 -1
- package/out-tsc/src/form/TembaSlider.js.map +1 -0
- package/out-tsc/src/{templates → form}/TemplateEditor.js +1 -1
- package/out-tsc/src/form/TemplateEditor.js.map +1 -0
- package/out-tsc/src/{textinput → form}/TextInput.js +3 -3
- package/out-tsc/src/form/TextInput.js.map +1 -0
- package/out-tsc/src/{omnibox → form/select}/Omnibox.js +2 -2
- package/out-tsc/src/form/select/Omnibox.js.map +1 -0
- package/out-tsc/src/{select → form/select}/PopupSelect.js +1 -1
- package/out-tsc/src/form/select/PopupSelect.js.map +1 -0
- package/out-tsc/src/{select → form/select}/Select.js +86 -87
- package/out-tsc/src/form/select/Select.js.map +1 -0
- package/out-tsc/src/{select → form/select}/UserSelect.js +1 -1
- package/out-tsc/src/form/select/UserSelect.js.map +1 -0
- package/out-tsc/src/{select → form/select}/WorkspaceSelect.js +1 -1
- package/out-tsc/src/form/select/WorkspaceSelect.js.map +1 -0
- package/out-tsc/src/interfaces.js +1 -0
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/layout/Dialog.js.map +1 -0
- package/out-tsc/src/layout/Mask.js.map +1 -0
- package/out-tsc/src/{dialog → layout}/Modax.js.map +1 -1
- package/out-tsc/src/layout/Resizer.js.map +1 -0
- package/out-tsc/src/layout/Tab.js.map +1 -0
- package/out-tsc/src/layout/TabPane.js.map +1 -0
- package/out-tsc/src/list/NotificationList.js +1 -1
- package/out-tsc/src/list/NotificationList.js.map +1 -1
- package/out-tsc/src/list/RunList.js +1 -1
- package/out-tsc/src/list/RunList.js.map +1 -1
- package/out-tsc/src/list/ShortcutList.js.map +1 -1
- package/out-tsc/src/list/TembaMenu.js +1 -1
- package/out-tsc/src/list/TembaMenu.js.map +1 -1
- package/out-tsc/src/list/TicketList.js +1 -1
- package/out-tsc/src/list/TicketList.js.map +1 -1
- package/out-tsc/src/{aliaseditor → live}/AliasEditor.js +1 -1
- package/out-tsc/src/live/AliasEditor.js.map +1 -0
- package/out-tsc/src/{contacts → live}/ContactBadges.js +1 -1
- package/out-tsc/src/live/ContactBadges.js.map +1 -0
- package/out-tsc/src/{contacts → live}/ContactChat.js +79 -3
- package/out-tsc/src/live/ContactChat.js.map +1 -0
- package/out-tsc/src/{contacts → live}/ContactDetails.js +1 -1
- package/out-tsc/src/live/ContactDetails.js.map +1 -0
- package/out-tsc/src/{contacts → live}/ContactFieldEditor.js +2 -2
- package/out-tsc/src/live/ContactFieldEditor.js.map +1 -0
- package/out-tsc/src/live/ContactFields.js.map +1 -0
- package/out-tsc/src/live/ContactNameFetch.js.map +1 -0
- package/out-tsc/src/{contacts → live}/ContactNotepad.js +1 -1
- package/out-tsc/src/{contacts → live}/ContactNotepad.js.map +1 -1
- package/out-tsc/src/{contacts → live}/ContactPending.js +1 -1
- package/out-tsc/src/live/ContactPending.js.map +1 -0
- package/out-tsc/src/live/ContactStoreElement.js.map +1 -0
- package/out-tsc/src/live/FieldManager.js.map +1 -0
- package/out-tsc/src/live/StartProgress.js.map +1 -0
- package/out-tsc/src/{chart → live}/TembaChart.js +5 -1
- package/out-tsc/src/live/TembaChart.js.map +1 -0
- package/out-tsc/src/locales/es.js +5 -5
- package/out-tsc/src/locales/es.js.map +1 -1
- package/out-tsc/src/locales/fr.js +5 -5
- package/out-tsc/src/locales/fr.js.map +1 -1
- package/out-tsc/src/locales/locale-codes.js +2 -11
- package/out-tsc/src/locales/locale-codes.js.map +1 -1
- package/out-tsc/src/locales/pt.js +5 -5
- package/out-tsc/src/locales/pt.js.map +1 -1
- package/out-tsc/src/store/AppState.js +54 -24
- package/out-tsc/src/store/AppState.js.map +1 -1
- package/out-tsc/src/store/Store.js +1 -1
- package/out-tsc/src/store/Store.js.map +1 -1
- package/out-tsc/src/{utils/index.js → utils.js} +22 -1
- package/out-tsc/src/utils.js.map +1 -0
- package/out-tsc/src/webchat/WebChat.js +1 -1
- package/out-tsc/src/webchat/WebChat.js.map +1 -1
- package/out-tsc/temba-components.js +2 -2
- package/out-tsc/temba-components.js.map +1 -1
- package/out-tsc/temba-modules.js +54 -54
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/temba-webchat.js +2 -2
- package/out-tsc/temba-webchat.js.map +1 -1
- package/out-tsc/test/temba-alert.test.js +1 -1
- package/out-tsc/test/temba-alert.test.js.map +1 -1
- package/out-tsc/test/temba-appstate-language.test.js +90 -0
- package/out-tsc/test/temba-appstate-language.test.js.map +1 -1
- package/out-tsc/test/temba-charcount.test.js.map +1 -1
- package/out-tsc/test/temba-chart.test.js +1 -1
- package/out-tsc/test/temba-chart.test.js.map +1 -1
- package/out-tsc/test/temba-checkbox.test.js.map +1 -1
- package/out-tsc/test/temba-color-picker.test.js +1 -1
- package/out-tsc/test/temba-color-picker.test.js.map +1 -1
- package/out-tsc/test/temba-completion.test.js +1 -1
- package/out-tsc/test/temba-completion.test.js.map +1 -1
- package/out-tsc/test/temba-compose.test.js +1 -1
- package/out-tsc/test/temba-compose.test.js.map +1 -1
- package/out-tsc/test/temba-contact-badges.test.js +1 -1
- package/out-tsc/test/temba-contact-badges.test.js.map +1 -1
- package/out-tsc/test/temba-contact-chat.test.js +3 -1
- package/out-tsc/test/temba-contact-chat.test.js.map +1 -1
- package/out-tsc/test/temba-contact-details.test.js +1 -1
- package/out-tsc/test/temba-contact-details.test.js.map +1 -1
- package/out-tsc/test/temba-contact-fields.test.js +1 -1
- package/out-tsc/test/temba-contact-fields.test.js.map +1 -1
- package/out-tsc/test/temba-contact-search.test.js +1 -1
- package/out-tsc/test/temba-contact-search.test.js.map +1 -1
- package/out-tsc/test/temba-date.test.js +5 -1
- package/out-tsc/test/temba-date.test.js.map +1 -1
- package/out-tsc/test/temba-datepicker.test.js +1 -1
- package/out-tsc/test/temba-datepicker.test.js.map +1 -1
- package/out-tsc/test/temba-dialog.test.js +1 -1
- package/out-tsc/test/temba-dialog.test.js.map +1 -1
- package/out-tsc/test/temba-dropdown.test.js +1 -1
- package/out-tsc/test/temba-dropdown.test.js.map +1 -1
- package/out-tsc/test/temba-excellent-helpers.test.js +316 -0
- package/out-tsc/test/temba-excellent-helpers.test.js.map +1 -0
- package/out-tsc/test/temba-field-manager.test.js.map +1 -1
- package/out-tsc/test/temba-flow-editor-node.test.js +414 -1
- package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
- package/out-tsc/test/temba-flow-editor.test.js +185 -0
- package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
- package/out-tsc/test/temba-flow-plumber-connections.test.js +113 -0
- package/out-tsc/test/temba-flow-plumber-connections.test.js.map +1 -0
- package/out-tsc/test/temba-flow-plumber.test.js +73 -93
- package/out-tsc/test/temba-flow-plumber.test.js.map +1 -1
- package/out-tsc/test/temba-flow-render.test.js +624 -1
- package/out-tsc/test/temba-flow-render.test.js.map +1 -1
- package/out-tsc/test/temba-flow-self-routing.test.js +172 -0
- package/out-tsc/test/temba-flow-self-routing.test.js.map +1 -0
- package/out-tsc/test/temba-formfield.test.js.map +1 -1
- package/out-tsc/test/temba-icon.test.js +1 -1
- package/out-tsc/test/temba-icon.test.js.map +1 -1
- package/out-tsc/test/temba-integration-markdown.test.js.map +1 -1
- package/out-tsc/test/temba-label.test.js +1 -1
- package/out-tsc/test/temba-label.test.js.map +1 -1
- package/out-tsc/test/temba-lightbox.test.js +1 -1
- package/out-tsc/test/temba-lightbox.test.js.map +1 -1
- package/out-tsc/test/temba-markdown.test.js +127 -0
- package/out-tsc/test/temba-markdown.test.js.map +1 -0
- package/out-tsc/test/temba-menu.test.js +1 -1
- package/out-tsc/test/temba-menu.test.js.map +1 -1
- package/out-tsc/test/temba-modax.test.js +1 -1
- package/out-tsc/test/temba-modax.test.js.map +1 -1
- package/out-tsc/test/temba-modules.test.js +47 -0
- package/out-tsc/test/temba-modules.test.js.map +1 -0
- package/out-tsc/test/temba-omnibox.test.js +1 -1
- package/out-tsc/test/temba-omnibox.test.js.map +1 -1
- package/out-tsc/test/temba-options.test.js.map +1 -1
- package/out-tsc/test/temba-range-picker.test.js +9 -2
- package/out-tsc/test/temba-range-picker.test.js.map +1 -1
- package/out-tsc/test/temba-rapid-element.test.js +273 -0
- package/out-tsc/test/temba-rapid-element.test.js.map +1 -0
- package/out-tsc/test/temba-resize-element.test.js +85 -0
- package/out-tsc/test/temba-resize-element.test.js.map +1 -0
- package/out-tsc/test/temba-select.test.js +2 -2
- package/out-tsc/test/temba-select.test.js.map +1 -1
- package/out-tsc/test/temba-slider.test.js.map +1 -1
- package/out-tsc/test/temba-sticky-note.test.js +194 -0
- package/out-tsc/test/temba-sticky-note.test.js.map +1 -0
- package/out-tsc/test/temba-template-editor.test.js.map +1 -1
- package/out-tsc/test/temba-textinput.test.js +1 -1
- package/out-tsc/test/temba-textinput.test.js.map +1 -1
- package/out-tsc/test/temba-tip.test.js +1 -1
- package/out-tsc/test/temba-tip.test.js.map +1 -1
- package/out-tsc/test/temba-toast.test.js +1 -1
- package/out-tsc/test/temba-toast.test.js.map +1 -1
- package/out-tsc/test/temba-utils-index.test.js +1 -1
- package/out-tsc/test/temba-utils-index.test.js.map +1 -1
- package/out-tsc/test/temba-utils-uuid.test.js +38 -0
- package/out-tsc/test/temba-utils-uuid.test.js.map +1 -0
- package/out-tsc/test/temba-webchat.test.js +28 -12
- package/out-tsc/test/temba-webchat.test.js.map +1 -1
- package/out-tsc/test/utils.test.js +2 -6
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +18 -9
- package/rollup.components.mjs +1 -1
- package/screenshots/truth/datepicker/range-picker-all.png +0 -0
- package/screenshots/truth/datepicker/range-picker-button-states.png +0 -0
- package/screenshots/truth/datepicker/range-picker-default.png +0 -0
- package/screenshots/truth/datepicker/range-picker-editing-start.png +0 -0
- package/screenshots/truth/datepicker/range-picker-initial-values.png +0 -0
- package/screenshots/truth/datepicker/range-picker-week.png +0 -0
- package/screenshots/truth/datepicker/range-picker-year.png +0 -0
- package/screenshots/truth/sticky-note/blue-color.png +0 -0
- package/screenshots/truth/sticky-note/blue.png +0 -0
- package/screenshots/truth/sticky-note/color-picker-expanded.png +0 -0
- package/screenshots/truth/sticky-note/default.png +0 -0
- package/screenshots/truth/sticky-note/gray-color.png +0 -0
- package/screenshots/truth/sticky-note/gray.png +0 -0
- package/screenshots/truth/sticky-note/green-color.png +0 -0
- package/screenshots/truth/sticky-note/green.png +0 -0
- package/screenshots/truth/sticky-note/pink-color.png +0 -0
- package/screenshots/truth/sticky-note/pink.png +0 -0
- package/screenshots/truth/sticky-note/yellow-color.png +0 -0
- package/screenshots/truth/sticky-note/yellow.png +0 -0
- package/src/{charcount → display}/CharCount.ts +164 -2
- package/src/{vectoricon/VectorIcon.ts → display/Icon.ts} +1 -1
- package/src/{leafletmap → display}/LeafletMap.ts +19 -1
- package/src/{thumbnail → display}/Thumbnail.ts +1 -1
- package/src/{tip → display}/Tip.ts +1 -2
- package/src/{contacts/events.ts → events.ts} +1 -64
- package/src/flow/Editor.ts +655 -165
- package/src/flow/EditorNode.ts +337 -22
- package/src/flow/Plumber.ts +186 -79
- package/src/flow/StickyNote.ts +165 -9
- package/src/flow/config.ts +114 -18
- package/src/flow/render.ts +398 -11
- package/src/{checkbox → form}/Checkbox.ts +2 -2
- package/src/{colorpicker → form}/ColorPicker.ts +2 -2
- package/src/{completion → form}/Completion.ts +3 -3
- package/src/{compose → form}/Compose.ts +7 -7
- package/src/{contactsearch → form}/ContactSearch.ts +6 -6
- package/src/{datepicker → form}/DatePicker.ts +1 -1
- package/src/{FormElement.ts → form/FormElement.ts} +1 -1
- package/src/{imagepicker → form}/ImagePicker.ts +2 -2
- package/src/{mediapicker → form}/MediaPicker.ts +1 -1
- package/src/{datepicker → form}/RangePicker.ts +3 -2
- package/src/{slider → form}/TembaSlider.ts +1 -1
- package/src/{templates → form}/TemplateEditor.ts +2 -2
- package/src/{textinput → form}/TextInput.ts +5 -5
- package/src/{omnibox → form/select}/Omnibox.ts +2 -2
- package/src/{select → form/select}/PopupSelect.ts +1 -1
- package/src/{select → form/select}/Select.ts +124 -126
- package/src/{select → form/select}/UserSelect.ts +1 -1
- package/src/{select → form/select}/WorkspaceSelect.ts +1 -1
- package/src/interfaces.ts +2 -1
- package/src/{dialog → layout}/Dialog.ts +1 -1
- package/src/list/NotificationList.ts +2 -2
- package/src/list/RunList.ts +3 -3
- package/src/list/ShortcutList.ts +1 -1
- package/src/list/TembaMenu.ts +2 -2
- package/src/list/TicketList.ts +1 -1
- package/src/{aliaseditor → live}/AliasEditor.ts +3 -3
- package/src/{contacts → live}/ContactBadges.ts +1 -1
- package/src/{contacts → live}/ContactChat.ts +118 -8
- package/src/{contacts → live}/ContactDetails.ts +1 -1
- package/src/{contacts → live}/ContactFieldEditor.ts +4 -4
- package/src/{contacts → live}/ContactFields.ts +1 -1
- package/src/{contacts → live}/ContactNotepad.ts +1 -1
- package/src/{contacts → live}/ContactPending.ts +1 -1
- package/src/{chart → live}/TembaChart.ts +5 -2
- package/src/locales/es.ts +13 -18
- package/src/locales/fr.ts +13 -18
- package/src/locales/locale-codes.ts +2 -11
- package/src/locales/pt.ts +13 -18
- package/src/store/AppState.ts +75 -29
- package/src/store/Store.ts +1 -1
- package/src/store/flow-definition.d.ts +125 -0
- package/src/{utils/index.ts → utils.ts} +26 -10
- package/src/webchat/WebChat.ts +1 -1
- package/static/css/temba-components.css +1 -0
- package/svg.js +1 -4
- package/temba-components.ts +2 -2
- package/temba-modules.ts +54 -54
- package/temba-webchat.ts +2 -2
- package/test/temba-alert.test.ts +1 -1
- package/test/temba-appstate-language.test.ts +108 -0
- package/test/temba-charcount.test.ts +1 -1
- package/test/temba-chart.test.ts +1 -1
- package/test/temba-checkbox.test.ts +1 -1
- package/test/temba-color-picker.test.ts +1 -1
- package/test/temba-completion.test.ts +1 -1
- package/test/temba-compose.test.ts +1 -1
- package/test/temba-contact-badges.test.ts +1 -1
- package/test/temba-contact-chat.test.ts +6 -4
- package/test/temba-contact-details.test.ts +1 -1
- package/test/temba-contact-fields.test.ts +1 -1
- package/test/temba-contact-search.test.ts +2 -2
- package/test/temba-date.test.ts +8 -3
- package/test/temba-datepicker.test.ts +1 -1
- package/test/temba-dialog.test.ts +1 -1
- package/test/temba-dropdown.test.ts +1 -1
- package/test/temba-excellent-helpers.test.ts +417 -0
- package/test/temba-field-manager.test.ts +2 -2
- package/test/temba-flow-editor-node.test.ts +536 -1
- package/test/temba-flow-editor.test.ts +224 -0
- package/test/temba-flow-editor.test.ts.backup +563 -0
- package/test/temba-flow-plumber-connections.test.ts +142 -0
- package/test/temba-flow-plumber.test.ts +83 -120
- package/test/temba-flow-render.test.ts +787 -4
- package/test/temba-flow-self-routing.test.ts +215 -0
- package/test/temba-formfield.test.ts +1 -1
- package/test/temba-icon.test.ts +1 -1
- package/test/temba-integration-markdown.test.ts +1 -1
- package/test/temba-label.test.ts +1 -1
- package/test/temba-lightbox.test.ts +1 -1
- package/test/temba-markdown.test.ts +162 -0
- package/test/temba-menu.test.ts +1 -1
- package/test/temba-modax.test.ts +2 -2
- package/test/temba-modules.test.ts +56 -0
- package/test/temba-omnibox.test.ts +1 -1
- package/test/temba-options.test.ts +1 -1
- package/test/temba-range-picker.test.ts +17 -2
- package/test/temba-rapid-element.test.ts +341 -0
- package/test/temba-resize-element.test.ts +104 -0
- package/test/temba-select.test.ts +2 -2
- package/test/temba-slider.test.ts +1 -1
- package/test/temba-sticky-note.test.ts +281 -0
- package/test/temba-template-editor.test.ts +1 -1
- package/test/temba-textinput.test.ts +1 -1
- package/test/temba-tip.test.ts +1 -1
- package/test/temba-toast.test.ts +1 -1
- package/test/temba-utils-index.test.ts +1 -1
- package/test/temba-utils-index.test.ts.backup +1737 -0
- package/test/temba-utils-uuid.test.ts +48 -0
- package/test/temba-webchat.test.ts +30 -12
- package/test/utils.test.ts +5 -9
- package/web-dev-server.config.mjs +1 -1
- package/demo/alert/example.html +0 -65
- package/demo/button/example.html +0 -71
- package/demo/chart/horizontal-demo.html +0 -81
- package/demo/checkbox/example.html +0 -72
- package/demo/compose/example.html +0 -72
- package/demo/dropdown/example.html +0 -99
- package/demo/progress/example.html +0 -59
- package/demo/select/drag-and-drop.html +0 -142
- package/demo/select/example.html +0 -82
- package/demo/select/multi.html +0 -73
- package/demo/slider/example.html +0 -59
- package/demo/tabs/example.html +0 -91
- package/demo/textinput/example.html +0 -61
- package/out-tsc/src/FormElement.js.map +0 -1
- package/out-tsc/src/alert/Alert.js.map +0 -1
- package/out-tsc/src/aliaseditor/AliasEditor.js.map +0 -1
- package/out-tsc/src/anchor/Anchor.js.map +0 -1
- package/out-tsc/src/button/Button.js.map +0 -1
- package/out-tsc/src/charcount/CharCount.js.map +0 -1
- package/out-tsc/src/charcount/helpers.js +0 -159
- package/out-tsc/src/charcount/helpers.js.map +0 -1
- package/out-tsc/src/chart/TembaChart.js.map +0 -1
- package/out-tsc/src/chat/Chat.js.map +0 -1
- package/out-tsc/src/checkbox/Checkbox.js.map +0 -1
- package/out-tsc/src/colorpicker/ColorPicker.js.map +0 -1
- package/out-tsc/src/completion/Completion.js.map +0 -1
- package/out-tsc/src/completion/ExcellentParser.js.map +0 -1
- package/out-tsc/src/completion/helpers.js.map +0 -1
- package/out-tsc/src/compose/Compose.js.map +0 -1
- package/out-tsc/src/contacts/ContactBadges.js.map +0 -1
- package/out-tsc/src/contacts/ContactChat.js.map +0 -1
- package/out-tsc/src/contacts/ContactDetails.js.map +0 -1
- package/out-tsc/src/contacts/ContactFieldEditor.js.map +0 -1
- package/out-tsc/src/contacts/ContactFields.js.map +0 -1
- package/out-tsc/src/contacts/ContactName.js.map +0 -1
- package/out-tsc/src/contacts/ContactNameFetch.js.map +0 -1
- package/out-tsc/src/contacts/ContactPending.js.map +0 -1
- package/out-tsc/src/contacts/ContactStoreElement.js.map +0 -1
- package/out-tsc/src/contacts/ContactUrn.js.map +0 -1
- package/out-tsc/src/contacts/events.js +0 -65
- package/out-tsc/src/contacts/events.js.map +0 -1
- package/out-tsc/src/contacts/helpers.js +0 -77
- package/out-tsc/src/contacts/helpers.js.map +0 -1
- package/out-tsc/src/contactsearch/ContactSearch.js.map +0 -1
- package/out-tsc/src/date/TembaDate.js.map +0 -1
- package/out-tsc/src/datepicker/DatePicker.js.map +0 -1
- package/out-tsc/src/datepicker/RangePicker.js.map +0 -1
- package/out-tsc/src/dialog/Dialog.js.map +0 -1
- package/out-tsc/src/dropdown/Dropdown.js.map +0 -1
- package/out-tsc/src/fields/FieldManager.js.map +0 -1
- package/out-tsc/src/formfield/FormField.js.map +0 -1
- package/out-tsc/src/imagepicker/CroppieCSS.js.map +0 -1
- package/out-tsc/src/imagepicker/ImagePicker.js.map +0 -1
- package/out-tsc/src/label/Label.js.map +0 -1
- package/out-tsc/src/leafletmap/LeafletMap.js.map +0 -1
- package/out-tsc/src/leafletmap/helpers.js +0 -17
- package/out-tsc/src/leafletmap/helpers.js.map +0 -1
- package/out-tsc/src/lightbox/Lightbox.js.map +0 -1
- package/out-tsc/src/mask/Mask.js.map +0 -1
- package/out-tsc/src/mediapicker/MediaPicker.js.map +0 -1
- package/out-tsc/src/omnibox/Omnibox.js.map +0 -1
- package/out-tsc/src/options/helpers.js +0 -28
- package/out-tsc/src/options/helpers.js.map +0 -1
- package/out-tsc/src/progress/ProgressBar.js.map +0 -1
- package/out-tsc/src/progress/StartProgress.js.map +0 -1
- package/out-tsc/src/resizer/Resizer.js.map +0 -1
- package/out-tsc/src/select/PopupSelect.js.map +0 -1
- package/out-tsc/src/select/Select.js.map +0 -1
- package/out-tsc/src/select/UserSelect.js.map +0 -1
- package/out-tsc/src/select/WorkspaceSelect.js.map +0 -1
- package/out-tsc/src/select/helpers.js +0 -1
- package/out-tsc/src/select/helpers.js.map +0 -1
- package/out-tsc/src/shadowless/Shadowless.js +0 -33
- package/out-tsc/src/shadowless/Shadowless.js.map +0 -1
- package/out-tsc/src/slider/TembaSlider.js.map +0 -1
- package/out-tsc/src/sms/gsmsplitter.js.map +0 -1
- package/out-tsc/src/sms/gsmvalidator.js.map +0 -1
- package/out-tsc/src/sms/index.js.map +0 -1
- package/out-tsc/src/sms/unicodesplitter.js.map +0 -1
- package/out-tsc/src/tabpane/Tab.js.map +0 -1
- package/out-tsc/src/tabpane/TabPane.js.map +0 -1
- package/out-tsc/src/templates/TemplateEditor.js.map +0 -1
- package/out-tsc/src/textinput/TextInput.js.map +0 -1
- package/out-tsc/src/textinput/helpers.js +0 -12
- package/out-tsc/src/textinput/helpers.js.map +0 -1
- package/out-tsc/src/thumbnail/Thumbnail.js.map +0 -1
- package/out-tsc/src/tip/Tip.js.map +0 -1
- package/out-tsc/src/tip/helpers.js +0 -7
- package/out-tsc/src/tip/helpers.js.map +0 -1
- package/out-tsc/src/toast/Toast.js.map +0 -1
- package/out-tsc/src/user/TembaUser.js.map +0 -1
- package/out-tsc/src/utils/index.js.map +0 -1
- package/out-tsc/src/vectoricon/VectorIcon.js.map +0 -1
- package/out-tsc/src/vectoricon/index.js.map +0 -1
- package/src/charcount/helpers.ts +0 -162
- package/src/contacts/helpers.ts +0 -103
- package/src/leafletmap/helpers.ts +0 -18
- package/src/options/helpers.ts +0 -37
- package/src/select/helpers.ts +0 -0
- package/src/shadowless/Shadowless.ts +0 -32
- package/src/textinput/helpers.ts +0 -11
- package/src/tip/helpers.ts +0 -7
- /package/out-tsc/src/{alert → display}/Alert.js +0 -0
- /package/out-tsc/src/{anchor → display}/Anchor.js +0 -0
- /package/out-tsc/src/{button → display}/Button.js +0 -0
- /package/out-tsc/src/{chat → display}/Chat.js +0 -0
- /package/out-tsc/src/{contacts → display}/ContactName.js +0 -0
- /package/out-tsc/src/{contacts → display}/ContactUrn.js +0 -0
- /package/out-tsc/src/{dropdown → display}/Dropdown.js +0 -0
- /package/out-tsc/src/{label → display}/Label.js +0 -0
- /package/out-tsc/src/{lightbox → display}/Lightbox.js +0 -0
- /package/out-tsc/src/{loading → display}/Loading.js +0 -0
- /package/out-tsc/src/{options → display}/Options.js +0 -0
- /package/out-tsc/src/{progress → display}/ProgressBar.js +0 -0
- /package/out-tsc/src/{date → display}/TembaDate.js +0 -0
- /package/out-tsc/src/{user → display}/TembaUser.js +0 -0
- /package/out-tsc/src/{thumbnail → display}/Thumbnail.js +0 -0
- /package/out-tsc/src/{toast → display}/Toast.js +0 -0
- /package/out-tsc/src/{sms → display/sms}/gsmsplitter.js +0 -0
- /package/out-tsc/src/{sms → display/sms}/gsmvalidator.js +0 -0
- /package/out-tsc/src/{sms → display/sms}/index.js +0 -0
- /package/out-tsc/src/{sms → display/sms}/unicodesplitter.js +0 -0
- /package/out-tsc/src/{completion → excellent}/ExcellentParser.js +0 -0
- /package/out-tsc/src/{completion → excellent}/helpers.js +0 -0
- /package/out-tsc/src/{imagepicker → form}/CroppieCSS.js +0 -0
- /package/out-tsc/src/{formfield → form}/FormField.js +0 -0
- /package/out-tsc/src/{dialog → layout}/Dialog.js +0 -0
- /package/out-tsc/src/{mask → layout}/Mask.js +0 -0
- /package/out-tsc/src/{dialog → layout}/Modax.js +0 -0
- /package/out-tsc/src/{resizer → layout}/Resizer.js +0 -0
- /package/out-tsc/src/{tabpane → layout}/Tab.js +0 -0
- /package/out-tsc/src/{tabpane → layout}/TabPane.js +0 -0
- /package/out-tsc/src/{contacts → live}/ContactFields.js +0 -0
- /package/out-tsc/src/{contacts → live}/ContactNameFetch.js +0 -0
- /package/out-tsc/src/{contacts → live}/ContactStoreElement.js +0 -0
- /package/out-tsc/src/{fields → live}/FieldManager.js +0 -0
- /package/out-tsc/src/{progress → live}/StartProgress.js +0 -0
- /package/src/{vectoricon/index.ts → Icons.ts} +0 -0
- /package/src/{alert → display}/Alert.ts +0 -0
- /package/src/{anchor → display}/Anchor.ts +0 -0
- /package/src/{button → display}/Button.ts +0 -0
- /package/src/{chat → display}/Chat.ts +0 -0
- /package/src/{contacts → display}/ContactName.ts +0 -0
- /package/src/{contacts → display}/ContactUrn.ts +0 -0
- /package/src/{dropdown → display}/Dropdown.ts +0 -0
- /package/src/{label → display}/Label.ts +0 -0
- /package/src/{lightbox → display}/Lightbox.ts +0 -0
- /package/src/{loading → display}/Loading.ts +0 -0
- /package/src/{options → display}/Options.ts +0 -0
- /package/src/{progress → display}/ProgressBar.ts +0 -0
- /package/src/{date → display}/TembaDate.ts +0 -0
- /package/src/{user → display}/TembaUser.ts +0 -0
- /package/src/{toast → display}/Toast.ts +0 -0
- /package/src/{sms → display/sms}/gsmsplitter.ts +0 -0
- /package/src/{sms → display/sms}/gsmvalidator.ts +0 -0
- /package/src/{sms → display/sms}/index.ts +0 -0
- /package/src/{sms → display/sms}/unicodesplitter.ts +0 -0
- /package/src/{completion → excellent}/ExcellentParser.ts +0 -0
- /package/src/{completion → excellent}/helpers.ts +0 -0
- /package/src/{imagepicker → form}/CroppieCSS.ts +0 -0
- /package/src/{formfield → form}/FormField.ts +0 -0
- /package/src/{mask → layout}/Mask.ts +0 -0
- /package/src/{dialog → layout}/Modax.ts +0 -0
- /package/src/{resizer → layout}/Resizer.ts +0 -0
- /package/src/{tabpane → layout}/Tab.ts +0 -0
- /package/src/{tabpane → layout}/TabPane.ts +0 -0
- /package/src/{contacts → live}/ContactNameFetch.ts +0 -0
- /package/src/{contacts → live}/ContactStoreElement.ts +0 -0
- /package/src/{fields → live}/FieldManager.ts +0 -0
- /package/src/{progress → live}/StartProgress.ts +0 -0
|
@@ -5,13 +5,24 @@ import { property, state } from 'lit/decorators.js';
|
|
|
5
5
|
import { getStore } from '../store/Store';
|
|
6
6
|
import { fromStore, zustand } from '../store/AppState';
|
|
7
7
|
import { RapidElement } from '../RapidElement';
|
|
8
|
+
import { repeat } from 'lit-html/directives/repeat.js';
|
|
8
9
|
import { Plumber } from './Plumber';
|
|
9
10
|
import { EditorNode } from './EditorNode';
|
|
10
11
|
export function snapToGrid(value) {
|
|
11
|
-
|
|
12
|
+
const snapped = Math.round(value / 20) * 20;
|
|
13
|
+
return Math.max(snapped, 0);
|
|
14
|
+
}
|
|
15
|
+
export function findNodeForExit(definition, exitUuid) {
|
|
16
|
+
for (const node of definition.nodes) {
|
|
17
|
+
const exit = node.exits.find((e) => e.uuid === exitUuid);
|
|
18
|
+
if (exit) {
|
|
19
|
+
return node.uuid;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
12
23
|
}
|
|
13
24
|
const SAVE_QUIET_TIME = 500;
|
|
14
|
-
const DRAG_THRESHOLD =
|
|
25
|
+
const DRAG_THRESHOLD = 5;
|
|
15
26
|
export class Editor extends RapidElement {
|
|
16
27
|
// unfortunately, jsplumb requires that we be in light DOM
|
|
17
28
|
createRenderRoot() {
|
|
@@ -27,41 +38,23 @@ export class Editor extends RapidElement {
|
|
|
27
38
|
#grid {
|
|
28
39
|
position: relative;
|
|
29
40
|
background-color: #f9f9f9;
|
|
41
|
+
background-image: radial-gradient(
|
|
42
|
+
circle,
|
|
43
|
+
rgba(61, 177, 255, 0.3) 1px,
|
|
44
|
+
transparent 1px
|
|
45
|
+
);
|
|
46
|
+
background-size: 20px 20px;
|
|
30
47
|
background-position: 10px 10px;
|
|
31
|
-
background-image: linear-gradient(
|
|
32
|
-
0deg,
|
|
33
|
-
transparent 24%,
|
|
34
|
-
rgba(61, 177, 255, 0.15) 25%,
|
|
35
|
-
rgba(61, 177, 255, 0.15) 26%,
|
|
36
|
-
transparent 27%,
|
|
37
|
-
transparent 74%,
|
|
38
|
-
rgba(61, 177, 255, 0.15) 75%,
|
|
39
|
-
rgba(61, 177, 255, 0.15) 76%,
|
|
40
|
-
transparent 77%,
|
|
41
|
-
transparent
|
|
42
|
-
),
|
|
43
|
-
linear-gradient(
|
|
44
|
-
90deg,
|
|
45
|
-
transparent 24%,
|
|
46
|
-
rgba(61, 177, 255, 0.15) 25%,
|
|
47
|
-
rgba(61, 177, 255, 0.15) 26%,
|
|
48
|
-
transparent 27%,
|
|
49
|
-
transparent 74%,
|
|
50
|
-
rgba(61, 177, 255, 0.15) 75%,
|
|
51
|
-
rgba(61, 177, 255, 0.15) 76%,
|
|
52
|
-
transparent 77%,
|
|
53
|
-
transparent
|
|
54
|
-
);
|
|
55
|
-
background-size: 40px 40px;
|
|
56
48
|
box-shadow: inset -5px 0 10px rgba(0, 0, 0, 0.05);
|
|
57
49
|
border-top: 1px solid #e0e0e0;
|
|
58
|
-
display: inline-block;
|
|
59
50
|
width: 100%;
|
|
51
|
+
display: flex;
|
|
60
52
|
}
|
|
61
53
|
|
|
62
54
|
#canvas {
|
|
63
55
|
position: relative;
|
|
64
|
-
padding:
|
|
56
|
+
padding: 0px;
|
|
57
|
+
flex-grow: 1;
|
|
65
58
|
margin: 20px;
|
|
66
59
|
}
|
|
67
60
|
|
|
@@ -71,7 +64,7 @@ export class Editor extends RapidElement {
|
|
|
71
64
|
}
|
|
72
65
|
|
|
73
66
|
#canvas > .dragging {
|
|
74
|
-
z-index:
|
|
67
|
+
z-index: 99999 !important;
|
|
75
68
|
}
|
|
76
69
|
|
|
77
70
|
body .jtk-endpoint {
|
|
@@ -81,70 +74,44 @@ export class Editor extends RapidElement {
|
|
|
81
74
|
|
|
82
75
|
.jtk-endpoint {
|
|
83
76
|
z-index: 600;
|
|
77
|
+
opacity: 0;
|
|
84
78
|
}
|
|
85
79
|
|
|
86
80
|
.plumb-source {
|
|
87
81
|
z-index: 600;
|
|
88
|
-
|
|
82
|
+
cursor: pointer;
|
|
83
|
+
opacity: 0;
|
|
89
84
|
}
|
|
90
85
|
|
|
91
86
|
.plumb-source.connected {
|
|
92
|
-
box-shadow: 0 3px 3px 0px rgba(0, 0, 0, 0.1);
|
|
93
87
|
border-radius: 50%;
|
|
88
|
+
pointer-events: none;
|
|
94
89
|
}
|
|
95
90
|
|
|
96
91
|
.plumb-source circle {
|
|
97
|
-
fill:
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
.plumb-source.connected circle {
|
|
101
|
-
fill: #fff;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
.plumb-source svg {
|
|
105
|
-
fill: var(--color-connectors) !important;
|
|
106
|
-
stroke: var(--color-connectors);
|
|
92
|
+
fill: purple;
|
|
107
93
|
}
|
|
108
94
|
|
|
109
95
|
.plumb-target {
|
|
110
|
-
margin-top: -6px;
|
|
111
96
|
z-index: 600;
|
|
112
97
|
opacity: 0;
|
|
113
98
|
cursor: pointer;
|
|
99
|
+
fill: transparent;
|
|
114
100
|
}
|
|
115
101
|
|
|
116
|
-
body .plumb-connector path {
|
|
102
|
+
body svg.jtk-connector.plumb-connector path {
|
|
117
103
|
stroke: var(--color-connectors) !important;
|
|
118
104
|
stroke-width: 3px;
|
|
119
|
-
z-index: 10;
|
|
120
105
|
}
|
|
121
106
|
|
|
122
107
|
body .plumb-connector {
|
|
123
|
-
z-index: 10;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
body .plumb-connector.elevated {
|
|
127
|
-
z-index: 550;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
body .plumb-connector.elevated path {
|
|
131
|
-
stroke: var(--color-connectors) !important;
|
|
132
|
-
stroke-width: 3px;
|
|
133
|
-
z-index: 550;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
body .plumb-connector.elevated .plumb-arrow {
|
|
137
|
-
fill: var(--color-connectors);
|
|
138
|
-
stroke: var(--color-connectors);
|
|
139
|
-
stroke-width: 0px;
|
|
140
|
-
margin-top: 6px;
|
|
141
|
-
z-index: 550;
|
|
108
|
+
z-index: 10 !important;
|
|
142
109
|
}
|
|
143
110
|
|
|
144
111
|
body .plumb-connector .plumb-arrow {
|
|
145
112
|
fill: var(--color-connectors);
|
|
146
113
|
stroke: var(--color-connectors);
|
|
147
|
-
stroke-width: 0px;
|
|
114
|
+
stroke-width: 0px !important;
|
|
148
115
|
margin-top: 6px;
|
|
149
116
|
z-index: 10;
|
|
150
117
|
}
|
|
@@ -159,6 +126,50 @@ export class Editor extends RapidElement {
|
|
|
159
126
|
stroke-width: 0px;
|
|
160
127
|
z-index: 10;
|
|
161
128
|
}
|
|
129
|
+
|
|
130
|
+
/* Connection dragging feedback */
|
|
131
|
+
body svg.jtk-connector.jtk-dragging {
|
|
132
|
+
z-index: 99999 !important;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.katavorio-drag-no-select svg.jtk-connector path,
|
|
136
|
+
.katavorio-drag-no-select svg.jtk-endpoint path {
|
|
137
|
+
pointer-events: none !important;
|
|
138
|
+
border: 1px solid purple;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* Connection target feedback */
|
|
142
|
+
temba-flow-node.connection-target-valid {
|
|
143
|
+
outline: 3px solid var(--color-success, #22c55e) !important;
|
|
144
|
+
outline-offset: 2px;
|
|
145
|
+
border-radius: var(--curvature);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
temba-flow-node.connection-target-invalid {
|
|
149
|
+
outline: 3px solid var(--color-error, #ef4444) !important;
|
|
150
|
+
outline-offset: 2px;
|
|
151
|
+
border-radius: var(--curvature);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* Selection box styles */
|
|
155
|
+
.selection-box {
|
|
156
|
+
position: absolute;
|
|
157
|
+
border: 2px dashed #6298f0ff;
|
|
158
|
+
background-color: rgba(59, 130, 246, 0.1);
|
|
159
|
+
z-index: 9999;
|
|
160
|
+
pointer-events: none;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* Selected item styles */
|
|
164
|
+
.draggable.selected {
|
|
165
|
+
outline: 3px solid #6298f0ff;
|
|
166
|
+
outline-offset: 0px;
|
|
167
|
+
border-radius: var(--curvature);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.jtk-floating-endpoint {
|
|
171
|
+
pointer-events: none;
|
|
172
|
+
}
|
|
162
173
|
`;
|
|
163
174
|
}
|
|
164
175
|
constructor() {
|
|
@@ -171,9 +182,21 @@ export class Editor extends RapidElement {
|
|
|
171
182
|
this.dragStartPos = { x: 0, y: 0 };
|
|
172
183
|
this.currentDragItem = null;
|
|
173
184
|
this.startPos = { left: 0, top: 0 };
|
|
185
|
+
// Selection state
|
|
186
|
+
this.selectedItems = new Set();
|
|
187
|
+
this.isSelecting = false;
|
|
188
|
+
this.selectionBox = null;
|
|
189
|
+
this.targetId = null;
|
|
190
|
+
this.sourceId = null;
|
|
191
|
+
this.dragFromNodeId = null;
|
|
192
|
+
this.isValidTarget = true;
|
|
193
|
+
this.canvasMouseDown = false;
|
|
174
194
|
// Bound event handlers to maintain proper 'this' context
|
|
175
195
|
this.boundMouseMove = this.handleMouseMove.bind(this);
|
|
176
196
|
this.boundMouseUp = this.handleMouseUp.bind(this);
|
|
197
|
+
this.boundGlobalMouseDown = this.handleGlobalMouseDown.bind(this);
|
|
198
|
+
this.boundKeyDown = this.handleKeyDown.bind(this);
|
|
199
|
+
this.boundCanvasDoubleClick = this.handleCanvasDoubleClick.bind(this);
|
|
177
200
|
}
|
|
178
201
|
firstUpdated(changes) {
|
|
179
202
|
super.firstUpdated(changes);
|
|
@@ -182,6 +205,37 @@ export class Editor extends RapidElement {
|
|
|
182
205
|
if (changes.has('flow')) {
|
|
183
206
|
getStore().getState().fetchRevision(`/flow/revisions/${this.flow}`);
|
|
184
207
|
}
|
|
208
|
+
this.plumber.on('connection:drag', (info) => {
|
|
209
|
+
this.dragFromNodeId = document
|
|
210
|
+
.getElementById(info.sourceId)
|
|
211
|
+
.closest('.node').id;
|
|
212
|
+
this.sourceId = info.sourceId;
|
|
213
|
+
});
|
|
214
|
+
this.plumber.on('connection:abort', () => {
|
|
215
|
+
this.makeConnection();
|
|
216
|
+
});
|
|
217
|
+
this.plumber.on('connection:detach', () => {
|
|
218
|
+
this.makeConnection();
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
makeConnection() {
|
|
222
|
+
if (this.sourceId && this.targetId && this.isValidTarget) {
|
|
223
|
+
this.plumber.connectIds(this.dragFromNodeId, this.sourceId, this.targetId);
|
|
224
|
+
getStore()
|
|
225
|
+
.getState()
|
|
226
|
+
.updateConnection(this.dragFromNodeId, this.sourceId, this.targetId);
|
|
227
|
+
setTimeout(() => {
|
|
228
|
+
this.plumber.repaintEverything();
|
|
229
|
+
}, 100);
|
|
230
|
+
}
|
|
231
|
+
// Clean up visual feedback
|
|
232
|
+
document.querySelectorAll('temba-flow-node').forEach((node) => {
|
|
233
|
+
node.classList.remove('connection-target-valid', 'connection-target-invalid');
|
|
234
|
+
});
|
|
235
|
+
this.sourceId = null;
|
|
236
|
+
this.targetId = null;
|
|
237
|
+
this.dragFromNodeId = null;
|
|
238
|
+
this.isValidTarget = true;
|
|
185
239
|
}
|
|
186
240
|
updated(changes) {
|
|
187
241
|
super.updated(changes);
|
|
@@ -227,10 +281,22 @@ export class Editor extends RapidElement {
|
|
|
227
281
|
}
|
|
228
282
|
document.removeEventListener('mousemove', this.boundMouseMove);
|
|
229
283
|
document.removeEventListener('mouseup', this.boundMouseUp);
|
|
284
|
+
document.removeEventListener('mousedown', this.boundGlobalMouseDown);
|
|
285
|
+
document.removeEventListener('keydown', this.boundKeyDown);
|
|
286
|
+
const canvas = this.querySelector('#canvas');
|
|
287
|
+
if (canvas) {
|
|
288
|
+
canvas.removeEventListener('dblclick', this.boundCanvasDoubleClick);
|
|
289
|
+
}
|
|
230
290
|
}
|
|
231
291
|
setupGlobalEventListeners() {
|
|
232
292
|
document.addEventListener('mousemove', this.boundMouseMove);
|
|
233
293
|
document.addEventListener('mouseup', this.boundMouseUp);
|
|
294
|
+
document.addEventListener('mousedown', this.boundGlobalMouseDown);
|
|
295
|
+
document.addEventListener('keydown', this.boundKeyDown);
|
|
296
|
+
const canvas = this.querySelector('#canvas');
|
|
297
|
+
if (canvas) {
|
|
298
|
+
canvas.addEventListener('dblclick', this.boundCanvasDoubleClick);
|
|
299
|
+
}
|
|
234
300
|
}
|
|
235
301
|
getPosition(uuid, type) {
|
|
236
302
|
var _a, _b, _c;
|
|
@@ -241,24 +307,10 @@ export class Editor extends RapidElement {
|
|
|
241
307
|
return (_c = (_b = this.definition._ui.stickies) === null || _b === void 0 ? void 0 : _b[uuid]) === null || _c === void 0 ? void 0 : _c.position;
|
|
242
308
|
}
|
|
243
309
|
}
|
|
244
|
-
updatePosition(uuid, type, position) {
|
|
245
|
-
var _a;
|
|
246
|
-
if (type === 'node') {
|
|
247
|
-
getStore().getState().updateNodePosition(uuid, position);
|
|
248
|
-
}
|
|
249
|
-
else {
|
|
250
|
-
const currentSticky = (_a = this.definition._ui.stickies) === null || _a === void 0 ? void 0 : _a[uuid];
|
|
251
|
-
if (currentSticky) {
|
|
252
|
-
getStore()
|
|
253
|
-
.getState()
|
|
254
|
-
.updateStickyNote(uuid, {
|
|
255
|
-
...currentSticky,
|
|
256
|
-
position
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
310
|
handleMouseDown(event) {
|
|
311
|
+
// ignore right clicks
|
|
312
|
+
if (event.button !== 0)
|
|
313
|
+
return;
|
|
262
314
|
const element = event.currentTarget;
|
|
263
315
|
// Only start dragging if clicking on the element itself, not on exits or other interactive elements
|
|
264
316
|
const target = event.target;
|
|
@@ -270,7 +322,17 @@ export class Editor extends RapidElement {
|
|
|
270
322
|
const position = this.getPosition(uuid, type);
|
|
271
323
|
if (!position)
|
|
272
324
|
return;
|
|
273
|
-
//
|
|
325
|
+
// If clicking on a non-selected item, clear selection unless Ctrl/Cmd is held
|
|
326
|
+
if (!this.selectedItems.has(uuid) && !event.ctrlKey && !event.metaKey) {
|
|
327
|
+
this.selectedItems.clear();
|
|
328
|
+
// Don't add single items to selection - single clicks just clear existing selection
|
|
329
|
+
}
|
|
330
|
+
else if (!this.selectedItems.has(uuid)) {
|
|
331
|
+
// Add this item to selection only if Ctrl/Cmd is held
|
|
332
|
+
this.selectedItems.add(uuid);
|
|
333
|
+
}
|
|
334
|
+
// Always set up drag state regardless of selection status
|
|
335
|
+
// This allows single nodes to be dragged without being selected
|
|
274
336
|
this.isMouseDown = true;
|
|
275
337
|
this.dragStartPos = { x: event.clientX, y: event.clientY };
|
|
276
338
|
this.startPos = { left: position.left, top: position.top };
|
|
@@ -283,7 +345,230 @@ export class Editor extends RapidElement {
|
|
|
283
345
|
event.preventDefault();
|
|
284
346
|
event.stopPropagation();
|
|
285
347
|
}
|
|
348
|
+
handleGlobalMouseDown(event) {
|
|
349
|
+
var _a;
|
|
350
|
+
// ignore right clicks
|
|
351
|
+
if (event.button !== 0)
|
|
352
|
+
return;
|
|
353
|
+
// Check if the click is within our canvas
|
|
354
|
+
const canvasRect = (_a = this.querySelector('#grid')) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
355
|
+
if (!canvasRect)
|
|
356
|
+
return;
|
|
357
|
+
const isWithinCanvas = event.clientX >= canvasRect.left &&
|
|
358
|
+
event.clientX <= canvasRect.right &&
|
|
359
|
+
event.clientY >= canvasRect.top &&
|
|
360
|
+
event.clientY <= canvasRect.bottom;
|
|
361
|
+
if (!isWithinCanvas)
|
|
362
|
+
return;
|
|
363
|
+
// Check if we clicked on a draggable item (node or sticky)
|
|
364
|
+
const target = event.target;
|
|
365
|
+
const clickedOnDraggable = target.closest('.draggable');
|
|
366
|
+
if (clickedOnDraggable) {
|
|
367
|
+
// This is handled by the individual item mousedown handlers
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
// We clicked on empty canvas space, start selection
|
|
371
|
+
this.handleCanvasMouseDown(event);
|
|
372
|
+
}
|
|
373
|
+
handleCanvasMouseDown(event) {
|
|
374
|
+
var _a;
|
|
375
|
+
const target = event.target;
|
|
376
|
+
if (target.id === 'canvas' || target.id === 'grid') {
|
|
377
|
+
// Ignore clicks on exits
|
|
378
|
+
// Start selection box
|
|
379
|
+
this.canvasMouseDown = true;
|
|
380
|
+
this.dragStartPos = { x: event.clientX, y: event.clientY };
|
|
381
|
+
const canvasRect = (_a = this.querySelector('#canvas')) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
382
|
+
if (canvasRect) {
|
|
383
|
+
// Clear current selection
|
|
384
|
+
this.selectedItems.clear();
|
|
385
|
+
const relativeX = event.clientX - canvasRect.left;
|
|
386
|
+
const relativeY = event.clientY - canvasRect.top;
|
|
387
|
+
this.selectionBox = {
|
|
388
|
+
startX: relativeX,
|
|
389
|
+
startY: relativeY,
|
|
390
|
+
endX: relativeX,
|
|
391
|
+
endY: relativeY
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
event.preventDefault();
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
handleKeyDown(event) {
|
|
398
|
+
if (event.key === 'Delete' || event.key === 'Backspace') {
|
|
399
|
+
if (this.selectedItems.size > 0) {
|
|
400
|
+
this.showDeleteConfirmation();
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
if (event.key === 'Escape') {
|
|
404
|
+
this.selectedItems.clear();
|
|
405
|
+
this.requestUpdate();
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
showDeleteConfirmation() {
|
|
409
|
+
const itemCount = this.selectedItems.size;
|
|
410
|
+
const itemType = itemCount === 1 ? 'item' : 'items';
|
|
411
|
+
// Create and show confirmation dialog
|
|
412
|
+
const dialog = document.createElement('temba-dialog');
|
|
413
|
+
dialog.header = 'Delete Items';
|
|
414
|
+
dialog.primaryButtonName = 'Delete';
|
|
415
|
+
dialog.cancelButtonName = 'Cancel';
|
|
416
|
+
dialog.destructive = true;
|
|
417
|
+
dialog.innerHTML = `<div style="padding: 20px;">Are you sure you want to delete ${itemCount} ${itemType}?</div>`;
|
|
418
|
+
dialog.addEventListener('temba-button-clicked', (event) => {
|
|
419
|
+
if (event.detail.button.name === 'Delete') {
|
|
420
|
+
this.deleteSelectedItems();
|
|
421
|
+
dialog.open = false;
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
// Add to document and show
|
|
425
|
+
document.body.appendChild(dialog);
|
|
426
|
+
dialog.open = true;
|
|
427
|
+
// Clean up dialog when closed
|
|
428
|
+
dialog.addEventListener('temba-dialog-hidden', () => {
|
|
429
|
+
document.body.removeChild(dialog);
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
deleteNodes(uuids) {
|
|
433
|
+
// Clean up jsPlumb connections for nodes before removing them
|
|
434
|
+
uuids.forEach((uuid) => {
|
|
435
|
+
this.plumber.removeNodeConnections(uuid);
|
|
436
|
+
});
|
|
437
|
+
// Now remove them from the definition
|
|
438
|
+
if (uuids.length > 0 && this.plumber) {
|
|
439
|
+
getStore().getState().removeNodes(uuids);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
deleteSelectedItems() {
|
|
443
|
+
const nodes = Array.from(this.selectedItems).filter((uuid) => this.definition.nodes.some((node) => node.uuid === uuid));
|
|
444
|
+
this.deleteNodes(Array.from(nodes));
|
|
445
|
+
const stickies = Array.from(this.selectedItems).filter((uuid) => { var _a, _b; return (_b = (_a = this.definition._ui) === null || _a === void 0 ? void 0 : _a.stickies) === null || _b === void 0 ? void 0 : _b[uuid]; });
|
|
446
|
+
getStore().getState().removeStickyNotes(stickies);
|
|
447
|
+
// Clear selection
|
|
448
|
+
this.selectedItems.clear();
|
|
449
|
+
}
|
|
450
|
+
updateSelectionBox(event) {
|
|
451
|
+
var _a;
|
|
452
|
+
if (!this.selectionBox || !this.canvasMouseDown)
|
|
453
|
+
return;
|
|
454
|
+
const canvasRect = (_a = this.querySelector('#canvas')) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
455
|
+
if (!canvasRect)
|
|
456
|
+
return;
|
|
457
|
+
const relativeX = event.clientX - canvasRect.left;
|
|
458
|
+
const relativeY = event.clientY - canvasRect.top;
|
|
459
|
+
this.selectionBox = {
|
|
460
|
+
...this.selectionBox,
|
|
461
|
+
endX: relativeX,
|
|
462
|
+
endY: relativeY
|
|
463
|
+
};
|
|
464
|
+
// Update selected items based on selection box
|
|
465
|
+
this.updateSelectedItemsFromBox();
|
|
466
|
+
}
|
|
467
|
+
updateSelectedItemsFromBox() {
|
|
468
|
+
var _a, _b, _c;
|
|
469
|
+
if (!this.selectionBox)
|
|
470
|
+
return;
|
|
471
|
+
const newSelection = new Set();
|
|
472
|
+
const boxLeft = Math.min(this.selectionBox.startX, this.selectionBox.endX);
|
|
473
|
+
const boxTop = Math.min(this.selectionBox.startY, this.selectionBox.endY);
|
|
474
|
+
const boxRight = Math.max(this.selectionBox.startX, this.selectionBox.endX);
|
|
475
|
+
const boxBottom = Math.max(this.selectionBox.startY, this.selectionBox.endY);
|
|
476
|
+
// Check nodes
|
|
477
|
+
(_a = this.definition) === null || _a === void 0 ? void 0 : _a.nodes.forEach((node) => {
|
|
478
|
+
var _a, _b;
|
|
479
|
+
const nodeElement = this.querySelector(`[id="${node.uuid}"]`);
|
|
480
|
+
if (nodeElement) {
|
|
481
|
+
const position = (_a = this.definition._ui.nodes[node.uuid]) === null || _a === void 0 ? void 0 : _a.position;
|
|
482
|
+
if (position) {
|
|
483
|
+
const rect = nodeElement.getBoundingClientRect();
|
|
484
|
+
const canvasRect = (_b = this.querySelector('#canvas')) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect();
|
|
485
|
+
if (canvasRect) {
|
|
486
|
+
const nodeLeft = position.left;
|
|
487
|
+
const nodeTop = position.top;
|
|
488
|
+
const nodeRight = nodeLeft + rect.width;
|
|
489
|
+
const nodeBottom = nodeTop + rect.height;
|
|
490
|
+
// Check if selection box intersects with node
|
|
491
|
+
if (boxLeft < nodeRight &&
|
|
492
|
+
boxRight > nodeLeft &&
|
|
493
|
+
boxTop < nodeBottom &&
|
|
494
|
+
boxBottom > nodeTop) {
|
|
495
|
+
newSelection.add(node.uuid);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
// Check sticky notes
|
|
502
|
+
const stickies = ((_c = (_b = this.definition) === null || _b === void 0 ? void 0 : _b._ui) === null || _c === void 0 ? void 0 : _c.stickies) || {};
|
|
503
|
+
Object.entries(stickies).forEach(([uuid, sticky]) => {
|
|
504
|
+
if (sticky.position) {
|
|
505
|
+
const stickyElement = this.querySelector(`temba-sticky-note[uuid="${uuid}"]`);
|
|
506
|
+
if (stickyElement) {
|
|
507
|
+
// Use clientWidth/clientHeight instead of getBoundingClientRect() to get element dimensions
|
|
508
|
+
// This avoids the coordinate system mismatch between viewport and canvas coordinates
|
|
509
|
+
const width = stickyElement.clientWidth;
|
|
510
|
+
const height = stickyElement.clientHeight;
|
|
511
|
+
// Use the canvas coordinates from the sticky's position
|
|
512
|
+
const stickyLeft = sticky.position.left;
|
|
513
|
+
const stickyTop = sticky.position.top;
|
|
514
|
+
const stickyRight = stickyLeft + width;
|
|
515
|
+
const stickyBottom = stickyTop + height;
|
|
516
|
+
// Check if selection box intersects with sticky
|
|
517
|
+
if (boxLeft < stickyRight &&
|
|
518
|
+
boxRight > stickyLeft &&
|
|
519
|
+
boxTop < stickyBottom &&
|
|
520
|
+
boxBottom > stickyTop) {
|
|
521
|
+
newSelection.add(uuid);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
this.selectedItems = newSelection;
|
|
527
|
+
}
|
|
528
|
+
renderSelectionBox() {
|
|
529
|
+
if (!this.selectionBox || !this.isSelecting)
|
|
530
|
+
return '';
|
|
531
|
+
const left = Math.min(this.selectionBox.startX, this.selectionBox.endX);
|
|
532
|
+
const top = Math.min(this.selectionBox.startY, this.selectionBox.endY);
|
|
533
|
+
const width = Math.abs(this.selectionBox.endX - this.selectionBox.startX);
|
|
534
|
+
const height = Math.abs(this.selectionBox.endY - this.selectionBox.startY);
|
|
535
|
+
return html `<div
|
|
536
|
+
class="selection-box"
|
|
537
|
+
style="left: ${left}px; top: ${top}px; width: ${width}px; height: ${height}px;"
|
|
538
|
+
></div>`;
|
|
539
|
+
}
|
|
286
540
|
handleMouseMove(event) {
|
|
541
|
+
// Handle selection box drawing
|
|
542
|
+
if (this.canvasMouseDown && !this.isMouseDown) {
|
|
543
|
+
this.isSelecting = true;
|
|
544
|
+
this.updateSelectionBox(event);
|
|
545
|
+
this.requestUpdate(); // Force re-render
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
if (this.plumber.connectionDragging) {
|
|
549
|
+
const targetNode = document.querySelector('temba-flow-node:hover');
|
|
550
|
+
// Clear previous target styles
|
|
551
|
+
document.querySelectorAll('temba-flow-node').forEach((node) => {
|
|
552
|
+
node.classList.remove('connection-target-valid', 'connection-target-invalid');
|
|
553
|
+
});
|
|
554
|
+
if (targetNode) {
|
|
555
|
+
this.targetId = targetNode.getAttribute('uuid');
|
|
556
|
+
// Check if target is different from source node (prevent self-targeting)
|
|
557
|
+
this.isValidTarget = this.targetId !== this.dragFromNodeId;
|
|
558
|
+
// Apply visual feedback based on validity
|
|
559
|
+
if (this.isValidTarget) {
|
|
560
|
+
targetNode.classList.add('connection-target-valid');
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
targetNode.classList.add('connection-target-invalid');
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
this.targetId = null;
|
|
568
|
+
this.isValidTarget = true;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
// Handle item dragging
|
|
287
572
|
if (!this.isMouseDown || !this.currentDragItem)
|
|
288
573
|
return;
|
|
289
574
|
const deltaX = event.clientX - this.dragStartPos.x;
|
|
@@ -292,60 +577,96 @@ export class Editor extends RapidElement {
|
|
|
292
577
|
// Only start dragging if we've moved beyond the threshold
|
|
293
578
|
if (!this.isDragging && distance > DRAG_THRESHOLD) {
|
|
294
579
|
this.isDragging = true;
|
|
295
|
-
// If this is a node, elevate connections
|
|
296
|
-
if (this.currentDragItem.type === 'node' && this.plumber) {
|
|
297
|
-
this.plumber.elevateNodeConnections(this.currentDragItem.uuid);
|
|
298
|
-
}
|
|
299
580
|
}
|
|
300
581
|
// If we're actually dragging, update positions
|
|
301
582
|
if (this.isDragging) {
|
|
302
|
-
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
583
|
+
// Determine what items to move
|
|
584
|
+
const itemsToMove = this.selectedItems.has(this.currentDragItem.uuid) &&
|
|
585
|
+
this.selectedItems.size > 1
|
|
586
|
+
? Array.from(this.selectedItems)
|
|
587
|
+
: [this.currentDragItem.uuid];
|
|
588
|
+
itemsToMove.forEach((uuid) => {
|
|
589
|
+
const element = this.querySelector(`[uuid="${uuid}"]`);
|
|
590
|
+
if (element) {
|
|
591
|
+
const type = element.tagName === 'TEMBA-FLOW-NODE' ? 'node' : 'sticky';
|
|
592
|
+
const position = this.getPosition(uuid, type);
|
|
593
|
+
if (position) {
|
|
594
|
+
const newLeft = position.left + deltaX;
|
|
595
|
+
const newTop = position.top + deltaY;
|
|
596
|
+
// Update the visual position during drag
|
|
597
|
+
element.style.left = `${newLeft}px`;
|
|
598
|
+
element.style.top = `${newTop}px`;
|
|
599
|
+
// Add dragging class to ensure highest z-index
|
|
600
|
+
element.classList.add('dragging');
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
this.plumber.revalidate(itemsToMove);
|
|
311
605
|
}
|
|
312
606
|
}
|
|
313
607
|
handleMouseUp(event) {
|
|
608
|
+
// Handle selection box completion
|
|
609
|
+
if (this.canvasMouseDown && this.isSelecting) {
|
|
610
|
+
this.isSelecting = false;
|
|
611
|
+
this.selectionBox = null;
|
|
612
|
+
this.canvasMouseDown = false;
|
|
613
|
+
this.requestUpdate();
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
// Handle canvas click (clear selection)
|
|
617
|
+
if (this.canvasMouseDown && !this.isSelecting) {
|
|
618
|
+
this.canvasMouseDown = false;
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
// Handle item drag completion
|
|
314
622
|
if (!this.isMouseDown || !this.currentDragItem)
|
|
315
623
|
return;
|
|
316
624
|
// If we were actually dragging, handle the drag end
|
|
317
625
|
if (this.isDragging) {
|
|
318
|
-
// Restore normal z-index for node connections
|
|
319
|
-
if (this.currentDragItem.type === 'node' && this.plumber) {
|
|
320
|
-
this.plumber.restoreNodeConnections(this.currentDragItem.uuid);
|
|
321
|
-
}
|
|
322
626
|
const deltaX = event.clientX - this.dragStartPos.x;
|
|
323
627
|
const deltaY = event.clientY - this.dragStartPos.y;
|
|
324
|
-
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
628
|
+
// Determine what items were moved
|
|
629
|
+
const itemsToMove = this.selectedItems.has(this.currentDragItem.uuid) &&
|
|
630
|
+
this.selectedItems.size > 1
|
|
631
|
+
? Array.from(this.selectedItems)
|
|
632
|
+
: [this.currentDragItem.uuid];
|
|
633
|
+
// Update positions for all moved items
|
|
634
|
+
const newPositions = {};
|
|
635
|
+
itemsToMove.forEach((uuid) => {
|
|
636
|
+
const type = this.definition.nodes.find((node) => node.uuid === uuid)
|
|
637
|
+
? 'node'
|
|
638
|
+
: 'sticky';
|
|
639
|
+
const position = this.getPosition(uuid, type);
|
|
640
|
+
if (position) {
|
|
641
|
+
const newLeft = position.left + deltaX;
|
|
642
|
+
const newTop = position.top + deltaY;
|
|
643
|
+
// Snap to 20px grid for final position
|
|
644
|
+
const snappedLeft = snapToGrid(newLeft);
|
|
645
|
+
const snappedTop = snapToGrid(newTop);
|
|
646
|
+
const newPosition = { left: snappedLeft, top: snappedTop };
|
|
647
|
+
newPositions[uuid] = newPosition;
|
|
648
|
+
// Remove dragging class
|
|
649
|
+
const element = this.querySelector(`[uuid="${uuid}"]`);
|
|
650
|
+
if (element) {
|
|
651
|
+
element.classList.remove('dragging');
|
|
652
|
+
element.style.left = `${snappedLeft}px`;
|
|
653
|
+
element.style.top = `${snappedTop}px`;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
});
|
|
657
|
+
if (Object.keys(newPositions).length > 0) {
|
|
658
|
+
getStore().getState().updateCanvasPositions(newPositions);
|
|
659
|
+
setTimeout(() => {
|
|
660
|
+
this.plumber.repaintEverything();
|
|
661
|
+
}, 0);
|
|
343
662
|
}
|
|
663
|
+
this.selectedItems.clear();
|
|
344
664
|
}
|
|
345
665
|
// Reset all drag state
|
|
346
666
|
this.isDragging = false;
|
|
347
667
|
this.isMouseDown = false;
|
|
348
668
|
this.currentDragItem = null;
|
|
669
|
+
this.canvasMouseDown = false;
|
|
349
670
|
}
|
|
350
671
|
updateCanvasSize() {
|
|
351
672
|
var _a;
|
|
@@ -371,15 +692,54 @@ export class Editor extends RapidElement {
|
|
|
371
692
|
});
|
|
372
693
|
// Check sticky note positions
|
|
373
694
|
const stickies = ((_a = this.definition._ui) === null || _a === void 0 ? void 0 : _a.stickies) || {};
|
|
374
|
-
Object.
|
|
695
|
+
Object.entries(stickies).forEach(([uuid, sticky]) => {
|
|
375
696
|
if (sticky.position) {
|
|
376
|
-
|
|
377
|
-
|
|
697
|
+
const stickyElement = this.querySelector(`temba-sticky-note[uuid="${uuid}"]`);
|
|
698
|
+
if (stickyElement) {
|
|
699
|
+
// Use clientWidth/clientHeight instead of getBoundingClientRect() to get element dimensions
|
|
700
|
+
// This avoids the coordinate system mismatch between viewport and canvas coordinates
|
|
701
|
+
const width = stickyElement.clientWidth;
|
|
702
|
+
const height = stickyElement.clientHeight;
|
|
703
|
+
// Both sticky.position and width/height are now in the same coordinate system
|
|
704
|
+
maxWidth = Math.max(maxWidth, sticky.position.left + width);
|
|
705
|
+
maxHeight = Math.max(maxHeight, sticky.position.top + height);
|
|
706
|
+
}
|
|
707
|
+
else {
|
|
708
|
+
// Fallback to default sizes if element not found
|
|
709
|
+
maxWidth = Math.max(maxWidth, sticky.position.left + 200);
|
|
710
|
+
maxHeight = Math.max(maxHeight, sticky.position.top + 100);
|
|
711
|
+
}
|
|
378
712
|
}
|
|
379
713
|
});
|
|
380
714
|
// Update canvas size in store
|
|
381
715
|
store.getState().expandCanvas(maxWidth, maxHeight);
|
|
382
716
|
}
|
|
717
|
+
handleCanvasDoubleClick(event) {
|
|
718
|
+
// Check if we double-clicked on empty canvas space
|
|
719
|
+
const target = event.target;
|
|
720
|
+
if (target.id !== 'canvas') {
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
// Get canvas position
|
|
724
|
+
const canvas = this.querySelector('#canvas');
|
|
725
|
+
if (!canvas) {
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
const canvasRect = canvas.getBoundingClientRect();
|
|
729
|
+
const relativeX = event.clientX - canvasRect.left - 10;
|
|
730
|
+
const relativeY = event.clientY - canvasRect.top - 10;
|
|
731
|
+
// Snap position to grid
|
|
732
|
+
const snappedLeft = snapToGrid(relativeX);
|
|
733
|
+
const snappedTop = snapToGrid(relativeY);
|
|
734
|
+
// Create new sticky note
|
|
735
|
+
const store = getStore();
|
|
736
|
+
store.getState().createStickyNote({
|
|
737
|
+
left: snappedLeft,
|
|
738
|
+
top: snappedTop
|
|
739
|
+
});
|
|
740
|
+
event.preventDefault();
|
|
741
|
+
event.stopPropagation();
|
|
742
|
+
}
|
|
383
743
|
render() {
|
|
384
744
|
var _a, _b;
|
|
385
745
|
// we have to embed our own style since we are in light DOM
|
|
@@ -397,35 +757,47 @@ export class Editor extends RapidElement {
|
|
|
397
757
|
>
|
|
398
758
|
<div id="canvas">
|
|
399
759
|
${this.definition
|
|
400
|
-
? this.definition.nodes.
|
|
760
|
+
? repeat(this.definition.nodes, (node) => node.uuid, (node) => {
|
|
401
761
|
var _a;
|
|
402
762
|
const position = this.definition._ui.nodes[node.uuid].position;
|
|
403
|
-
const dragging = this.isDragging &&
|
|
763
|
+
const dragging = this.isDragging &&
|
|
764
|
+
((_a = this.currentDragItem) === null || _a === void 0 ? void 0 : _a.uuid) === node.uuid;
|
|
765
|
+
const selected = this.selectedItems.has(node.uuid);
|
|
404
766
|
return html `<temba-flow-node
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
767
|
+
class="draggable ${dragging ? 'dragging' : ''} ${selected
|
|
768
|
+
? 'selected'
|
|
769
|
+
: ''}"
|
|
770
|
+
@mousedown=${this.handleMouseDown.bind(this)}
|
|
771
|
+
uuid=${node.uuid}
|
|
772
|
+
style="left:${position.left}px; top:${position.top}px"
|
|
773
|
+
.plumber=${this.plumber}
|
|
774
|
+
.node=${node}
|
|
775
|
+
.ui=${this.definition._ui.nodes[node.uuid]}
|
|
776
|
+
@temba-node-deleted=${(event) => {
|
|
777
|
+
this.deleteNodes([event.detail.uuid]);
|
|
778
|
+
}}
|
|
779
|
+
></temba-flow-node>`;
|
|
413
780
|
})
|
|
414
781
|
: html `<temba-loading></temba-loading>`}
|
|
415
|
-
${Object.entries(stickies)
|
|
782
|
+
${repeat(Object.entries(stickies), ([uuid]) => uuid, ([uuid, sticky]) => {
|
|
416
783
|
var _a;
|
|
417
784
|
const position = sticky.position || { left: 0, top: 0 };
|
|
418
785
|
const dragging = this.isDragging && ((_a = this.currentDragItem) === null || _a === void 0 ? void 0 : _a.uuid) === uuid;
|
|
786
|
+
const selected = this.selectedItems.has(uuid);
|
|
419
787
|
return html `<temba-sticky-note
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
788
|
+
class="draggable ${dragging ? 'dragging' : ''} ${selected
|
|
789
|
+
? 'selected'
|
|
790
|
+
: ''}"
|
|
791
|
+
@mousedown=${this.handleMouseDown.bind(this)}
|
|
792
|
+
style="left:${position.left}px; top:${position.top}px; z-index: ${1000 +
|
|
423
793
|
position.top}"
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
794
|
+
uuid=${uuid}
|
|
795
|
+
.data=${sticky}
|
|
796
|
+
.dragging=${dragging}
|
|
797
|
+
.selected=${selected}
|
|
798
|
+
></temba-sticky-note>`;
|
|
428
799
|
})}
|
|
800
|
+
${this.renderSelectionBox()}
|
|
429
801
|
</div>
|
|
430
802
|
</div>
|
|
431
803
|
</div>`;
|
|
@@ -452,4 +824,25 @@ __decorate([
|
|
|
452
824
|
__decorate([
|
|
453
825
|
state()
|
|
454
826
|
], Editor.prototype, "currentDragItem", void 0);
|
|
827
|
+
__decorate([
|
|
828
|
+
state()
|
|
829
|
+
], Editor.prototype, "selectedItems", void 0);
|
|
830
|
+
__decorate([
|
|
831
|
+
state()
|
|
832
|
+
], Editor.prototype, "isSelecting", void 0);
|
|
833
|
+
__decorate([
|
|
834
|
+
state()
|
|
835
|
+
], Editor.prototype, "selectionBox", void 0);
|
|
836
|
+
__decorate([
|
|
837
|
+
state()
|
|
838
|
+
], Editor.prototype, "targetId", void 0);
|
|
839
|
+
__decorate([
|
|
840
|
+
state()
|
|
841
|
+
], Editor.prototype, "sourceId", void 0);
|
|
842
|
+
__decorate([
|
|
843
|
+
state()
|
|
844
|
+
], Editor.prototype, "dragFromNodeId", void 0);
|
|
845
|
+
__decorate([
|
|
846
|
+
state()
|
|
847
|
+
], Editor.prototype, "isValidTarget", void 0);
|
|
455
848
|
//# sourceMappingURL=Editor.js.map
|