@nyaruka/temba-components 0.142.2 → 0.142.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/temba-components.js +266 -192
  3. package/dist/temba-components.js.map +1 -1
  4. package/out-tsc/src/flow/CanvasMenu.js +8 -3
  5. package/out-tsc/src/flow/CanvasMenu.js.map +1 -1
  6. package/out-tsc/src/flow/CanvasNode.js +158 -9
  7. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  8. package/out-tsc/src/flow/Editor.js +473 -17
  9. package/out-tsc/src/flow/Editor.js.map +1 -1
  10. package/out-tsc/src/flow/NodeEditor.js +8 -8
  11. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  12. package/out-tsc/src/flow/Plumber.js +89 -27
  13. package/out-tsc/src/flow/Plumber.js.map +1 -1
  14. package/out-tsc/src/flow/StickyNote.js +63 -3
  15. package/out-tsc/src/flow/StickyNote.js.map +1 -1
  16. package/out-tsc/src/flow/actions/send_msg.js +11 -8
  17. package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
  18. package/out-tsc/src/flow/nodes/split_by_subflow.js +3 -1
  19. package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
  20. package/out-tsc/src/flow/utils.js +1 -3
  21. package/out-tsc/src/flow/utils.js.map +1 -1
  22. package/out-tsc/src/list/SortableList.js +104 -43
  23. package/out-tsc/src/list/SortableList.js.map +1 -1
  24. package/out-tsc/src/simulator/Simulator.js +5 -1
  25. package/out-tsc/src/simulator/Simulator.js.map +1 -1
  26. package/package.json +1 -1
  27. package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
  28. package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
  29. package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
  30. package/src/flow/CanvasMenu.ts +12 -4
  31. package/src/flow/CanvasNode.ts +185 -9
  32. package/src/flow/Editor.ts +552 -19
  33. package/src/flow/NodeEditor.ts +12 -12
  34. package/src/flow/Plumber.ts +101 -36
  35. package/src/flow/StickyNote.ts +76 -4
  36. package/src/flow/actions/send_msg.ts +11 -8
  37. package/src/flow/nodes/split_by_subflow.ts +3 -1
  38. package/src/flow/utils.ts +1 -3
  39. package/src/list/SortableList.ts +117 -47
  40. package/src/simulator/Simulator.ts +5 -1
@@ -1 +1 @@
1
- {"version":3,"file":"StickyNote.js","sourceRoot":"","sources":["../../../src/flow/StickyNote.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { RapidElement } from '../RapidElement';\nimport { StickyNote as StickyNoteData } from '../store/flow-definition';\nimport { getStore } from '../store/Store';\nimport { AppState, fromStore, zustand } from '../store/AppState';\n\nexport class StickyNote extends RapidElement {\n @property({ type: String })\n public uuid: string;\n\n @property({ type: Object })\n public data: StickyNoteData;\n\n @property({ type: Boolean })\n private dragging = false;\n\n @property({ type: Boolean })\n public selected = false;\n\n @property({ type: Boolean })\n private colorPickerExpanded = false;\n\n @fromStore(zustand, (state: AppState) => state.isTranslating)\n private isTranslating!: boolean;\n\n static get styles() {\n return css`\n :host {\n --sticky-color: #fef08a;\n --sticky-border-color: #facc15;\n --sticky-text-color: #451a03;\n --curvature: 8px;\n }\n\n .sticky-note {\n width: 182px;\n background-color: var(--sticky-color);\n border: 1px solid var(--sticky-border-color);\n border-radius: var(--curvature);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,\n sans-serif;\n font-size: 12px;\n overflow: hidden;\n transition: transform 0.1s ease, box-shadow 0.2s ease;\n color: var(--sticky-text-color);\n opacity: 0.85;\n }\n\n .sticky-note.dragging {\n opacity: 0.7;\n z-index: 1000;\n transform: rotate(0deg);\n box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);\n }\n\n .sticky-note:hover {\n transform: translateY(0px);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);\n }\n\n /* Color themes */\n .sticky-note.yellow {\n --sticky-color: #fef08a;\n --sticky-border-color: #facc15;\n --sticky-text-color: #451a03;\n }\n .sticky-note.blue {\n --sticky-color: #bfdbfe;\n --sticky-border-color: #3b82f6;\n --sticky-text-color: #1e3a8a;\n }\n .sticky-note.pink {\n --sticky-color: #fce7f3;\n --sticky-border-color: #ec4899;\n --sticky-text-color: #831843;\n }\n .sticky-note.green {\n --sticky-color: #d1fae5;\n --sticky-border-color: #10b981;\n --sticky-text-color: #064e3b;\n }\n .sticky-note.gray {\n --sticky-color: #f3f4f6;\n --sticky-border-color: #6b7280;\n --sticky-text-color: #374151;\n }\n\n /* Title and body containers */\n .sticky-title-container {\n position: relative;\n border-bottom: 1px solid var(--sticky-border-color);\n background-color: rgba(255, 255, 255, 0.5);\n display: flex;\n align-items: center;\n }\n .sticky-body-container {\n position: relative;\n }\n\n /* Editable fields */\n [contenteditable='true'] {\n margin: 2px;\n padding: 4px 8px;\n outline: none;\n border-radius: var(--curvature);\n transition: background 0.2s;\n }\n [contenteditable='true']:focus {\n background-color: rgba(255, 255, 255, 0.8);\n border-bottom: 1px solid rgba(0, 0, 0, 0.1);\n outline-color: var(--sticky-border-color);\n }\n\n /* Title */\n .sticky-title {\n font-weight: 600;\n font-size: 13px;\n color: var(--sticky-text-color);\n min-height: 20px;\n line-height: 20px;\n border-top-left-radius: var(--curvature);\n border-top-right-radius: var(--curvature);\n flex-grow: 1;\n padding: 4px 8px !important;\n margin: 2px;\n padding-left: 8px;\n }\n .sticky-title:empty::before {\n content: 'Click to add title';\n opacity: 0.5;\n font-style: italic;\n }\n .sticky-title:focus {\n border-bottom-left-radius: 0px;\n border-bottom-right-radius: 0px;\n }\n\n /* Body */\n .sticky-body {\n padding: 8px 10px;\n color: var(--sticky-text-color);\n line-height: 1.4;\n min-height: 48px;\n word-wrap: break-word;\n white-space: pre-wrap;\n margin: 2px;\n }\n .sticky-body:empty::before {\n content: 'Click to add note';\n opacity: 0.5;\n font-style: italic;\n }\n .sticky-body:focus {\n border-top-left-radius: 0px;\n border-top-right-radius: 0px;\n }\n\n /* Drag icon */\n .sticky-title-container > .drag-handle {\n --icon-color: var(--sticky-border-color);\n cursor: move;\n max-width: 20px;\n padding-left: 8px;\n overflow: hidden;\n transition: all 0.2s ease;\n }\n\n .sticky-note:hover .drag-handle {\n }\n\n .sticky-note:focus-within .sticky-title-container > .drag-handle {\n }\n\n /* Focus/active states */\n .sticky-note:focus-within {\n box-shadow: 0 0 0 1px var(--sticky-border-color),\n 0 10px 20px rgba(0, 0, 0, 0.3);\n }\n\n .sticky-note:focus-within .drag-handle {\n max-width: 0px;\n padding-left: 0px;\n }\n\n /* Color picker */\n .color-picker {\n position: absolute;\n bottom: 4px;\n right: 4px;\n width: 8px;\n height: 8px;\n border: 1px solid rgba(0, 0, 0, 0.2);\n\n border-radius: 3px;\n background-color: var(--sticky-color);\n cursor: pointer;\n transition: transform 0.2s ease;\n }\n\n .color-picker:hover {\n transform: scale(1.1);\n }\n\n .color-options {\n position: absolute;\n bottom: 0;\n right: 0;\n display: flex;\n gap: 4px;\n background-color: rgba(255, 255, 255, 0.9);\n border: 1px solid #ccc;\n border-radius: 6px;\n padding: 3px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n transform-origin: bottom right;\n transform: scale(0);\n opacity: 0;\n transition: transform 0.2s ease, opacity 0.2s ease;\n z-index: 1000;\n }\n\n .color-options.expanded {\n transform: scale(1);\n opacity: 1;\n }\n\n .color-option {\n width: 12px;\n height: 12px;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 3px;\n cursor: pointer;\n transition: transform 0.15s ease, border-color 0.15s ease;\n }\n\n .color-option:hover {\n transform: scale(1.1);\n border-color: rgba(0, 0, 0, 0.4);\n }\n\n .color-option.yellow {\n background-color: #fef08a;\n }\n\n .color-option.blue {\n background-color: #bfdbfe;\n }\n\n .color-option.pink {\n background-color: #fce7f3;\n }\n\n .color-option.green {\n background-color: #d1fae5;\n }\n\n .color-option.gray {\n background-color: #f3f4f6;\n }\n `;\n }\n\n protected updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n if (changes.has('data') || changes.has('uuid')) {\n this.updateCanvasSize();\n }\n }\n\n private updateCanvasSize(): void {\n if (!this.data) {\n return;\n }\n\n const element = this.querySelector('.sticky-note');\n if (element) {\n const rect = element.getBoundingClientRect();\n getStore()\n .getState()\n .expandCanvas(\n this.data.position.left + rect.width,\n this.data.position.top + rect.height\n );\n }\n }\n\n private handleTitleBlur(event: FocusEvent): void {\n const target = event.target as HTMLElement;\n const newTitle = target.textContent || '';\n\n if (this.data && newTitle !== this.data.title) {\n getStore()\n .getState()\n .updateStickyNote(this.uuid, {\n ...this.data,\n title: newTitle\n });\n }\n this.requestUpdate();\n }\n\n private handleBodyBlur(event: FocusEvent): void {\n const target = event.target as HTMLElement;\n const newBody = target.innerText || '';\n\n if (this.data && newBody !== this.data.body) {\n getStore()\n .getState()\n .updateStickyNote(this.uuid, {\n ...this.data,\n body: newBody\n });\n }\n this.requestUpdate();\n }\n\n private handleContentMouseDown(event: MouseEvent): void {\n // If this sticky note is selected, don't stop propagation\n // so that group dragging can work\n if (this.selected) {\n return;\n }\n // Otherwise, stop propagation to enable editing\n event.stopPropagation();\n }\n\n private handleTitleKeyDown(event: KeyboardEvent): void {\n if (event.key === 'Enter') {\n event.preventDefault();\n (event.target as HTMLElement).blur();\n }\n if (event.key === 'Escape') {\n (event.target as HTMLElement).blur();\n }\n }\n\n private handleBodyKeyDown(event: KeyboardEvent): void {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n (event.target as HTMLElement).blur();\n }\n if (event.key === 'Escape') {\n (event.target as HTMLElement).blur();\n }\n }\n\n private handleColorPickerMouseEnter(): void {\n this.colorPickerExpanded = true;\n }\n\n private handleColorPickerMouseLeave(): void {\n this.colorPickerExpanded = false;\n }\n\n private handleColorOptionClick(\n event: MouseEvent,\n color: 'yellow' | 'blue' | 'pink' | 'green' | 'gray'\n ): void {\n event.stopPropagation();\n\n if (this.data && color !== this.data.color) {\n getStore()\n .getState()\n .updateStickyNote(this.uuid, {\n ...this.data,\n color: color\n });\n }\n\n this.colorPickerExpanded = false;\n this.requestUpdate();\n }\n\n public render(): TemplateResult {\n if (!this.data) {\n return html`<div class=\"sticky-note\" style=\"display: none;\"></div>`;\n }\n\n const style = `left: ${this.data.position.left}px; top: ${this.data.position.top}px;`;\n\n return html`\n <div\n class=\"sticky-note ${this.data.color} ${this.dragging\n ? 'dragging'\n : ''}\"\n style=\"${style}\"\n data-uuid=\"${this.uuid}\"\n >\n <div class=\"sticky-title-container\">\n <temba-icon name=\"drag\" class=\"drag-handle\"></temba-icon>\n <div\n class=\"sticky-title\"\n contenteditable=\"${!this.isTranslating}\"\n @blur=\"${this.handleTitleBlur}\"\n @keydown=\"${this.handleTitleKeyDown}\"\n @mousedown=\"${this.handleContentMouseDown}\"\n .textContent=\"${this.data.title}\"\n ></div>\n </div>\n <div class=\"sticky-body-container\">\n <div\n class=\"sticky-body\"\n contenteditable=\"${!this.isTranslating}\"\n @blur=\"${this.handleBodyBlur}\"\n @keydown=\"${this.handleBodyKeyDown}\"\n @mousedown=\"${this.handleContentMouseDown}\"\n .textContent=\"${this.data.body}\"\n ></div>\n ${!this.isTranslating\n ? html`<div class=\"edit-icon\" title=\"Edit note\"></div>`\n : ''}\n\n <!-- Color picker -->\n <div\n class=\"color-picker\"\n @mouseenter=\"${this.handleColorPickerMouseEnter}\"\n @mouseleave=\"${this.handleColorPickerMouseLeave}\"\n >\n <div\n class=\"color-options ${this.colorPickerExpanded\n ? 'expanded'\n : ''}\"\n >\n <div\n class=\"color-option yellow\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'yellow')}\"\n ></div>\n <div\n class=\"color-option blue\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'blue')}\"\n ></div>\n <div\n class=\"color-option pink\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'pink')}\"\n ></div>\n <div\n class=\"color-option green\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'green')}\"\n ></div>\n <div\n class=\"color-option gray\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'gray')}\"\n ></div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n}\n"]}
1
+ {"version":3,"file":"StickyNote.js","sourceRoot":"","sources":["../../../src/flow/StickyNote.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { RapidElement } from '../RapidElement';\nimport { StickyNote as StickyNoteData } from '../store/flow-definition';\nimport { getStore } from '../store/Store';\nimport { AppState, fromStore, zustand } from '../store/AppState';\n\nexport class StickyNote extends RapidElement {\n @property({ type: String })\n public uuid: string;\n\n @property({ type: Object })\n public data: StickyNoteData;\n\n @property({ type: Boolean })\n private dragging = false;\n\n @property({ type: Boolean })\n public selected = false;\n\n @property({ type: Boolean })\n private colorPickerExpanded = false;\n\n // On touch devices, contenteditable starts false to prevent Apple Pencil\n // Scribble from hijacking touches. It is set to true on explicit tap.\n private isTouchDevice = navigator.maxTouchPoints > 0;\n private editingField: HTMLElement | null = null;\n\n @fromStore(zustand, (state: AppState) => state.isTranslating)\n private isTranslating!: boolean;\n\n static get styles() {\n return css`\n :host {\n --sticky-color: #fef08a;\n --sticky-border-color: #facc15;\n --sticky-text-color: #451a03;\n --curvature: 8px;\n }\n\n .sticky-note {\n width: 182px;\n background-color: var(--sticky-color);\n border: 1px solid var(--sticky-border-color);\n border-radius: var(--curvature);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,\n sans-serif;\n font-size: 12px;\n overflow: hidden;\n transition: transform 0.1s ease, box-shadow 0.2s ease;\n color: var(--sticky-text-color);\n opacity: 0.85;\n }\n\n .sticky-note.dragging {\n opacity: 0.7;\n z-index: 1000;\n transform: rotate(0deg);\n box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);\n }\n\n .sticky-note:hover {\n transform: translateY(0px);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);\n }\n\n /* Color themes */\n .sticky-note.yellow {\n --sticky-color: #fef08a;\n --sticky-border-color: #facc15;\n --sticky-text-color: #451a03;\n }\n .sticky-note.blue {\n --sticky-color: #bfdbfe;\n --sticky-border-color: #3b82f6;\n --sticky-text-color: #1e3a8a;\n }\n .sticky-note.pink {\n --sticky-color: #fce7f3;\n --sticky-border-color: #ec4899;\n --sticky-text-color: #831843;\n }\n .sticky-note.green {\n --sticky-color: #d1fae5;\n --sticky-border-color: #10b981;\n --sticky-text-color: #064e3b;\n }\n .sticky-note.gray {\n --sticky-color: #f3f4f6;\n --sticky-border-color: #6b7280;\n --sticky-text-color: #374151;\n }\n\n /* Title and body containers */\n .sticky-title-container {\n position: relative;\n border-bottom: 1px solid var(--sticky-border-color);\n background-color: rgba(255, 255, 255, 0.5);\n display: flex;\n align-items: center;\n }\n .sticky-body-container {\n position: relative;\n }\n\n /* Editable fields */\n [contenteditable='true'] {\n margin: 2px;\n padding: 4px 8px;\n outline: none;\n border-radius: var(--curvature);\n transition: background 0.2s;\n }\n [contenteditable='true']:focus {\n background-color: rgba(255, 255, 255, 0.8);\n border-bottom: 1px solid rgba(0, 0, 0, 0.1);\n outline-color: var(--sticky-border-color);\n }\n\n /* Title */\n .sticky-title {\n font-weight: 600;\n font-size: 13px;\n color: var(--sticky-text-color);\n min-height: 20px;\n line-height: 20px;\n border-top-left-radius: var(--curvature);\n border-top-right-radius: var(--curvature);\n flex-grow: 1;\n padding: 4px 8px !important;\n margin: 2px;\n padding-left: 8px;\n }\n .sticky-title:empty::before {\n content: 'Click to add title';\n opacity: 0.5;\n font-style: italic;\n }\n .sticky-title:focus {\n border-bottom-left-radius: 0px;\n border-bottom-right-radius: 0px;\n }\n\n /* Body */\n .sticky-body {\n padding: 8px 10px;\n color: var(--sticky-text-color);\n line-height: 1.4;\n min-height: 48px;\n word-wrap: break-word;\n white-space: pre-wrap;\n margin: 2px;\n }\n .sticky-body:empty::before {\n content: 'Click to add note';\n opacity: 0.5;\n font-style: italic;\n }\n .sticky-body:focus {\n border-top-left-radius: 0px;\n border-top-right-radius: 0px;\n }\n\n /* Drag icon */\n .sticky-title-container > .drag-handle {\n --icon-color: var(--sticky-border-color);\n cursor: move;\n max-width: 20px;\n padding-left: 8px;\n overflow: hidden;\n transition: all 0.2s ease;\n }\n\n .sticky-note:hover .drag-handle {\n }\n\n .sticky-note:focus-within .sticky-title-container > .drag-handle {\n }\n\n /* Focus/active states */\n .sticky-note:focus-within {\n box-shadow: 0 0 0 1px var(--sticky-border-color),\n 0 10px 20px rgba(0, 0, 0, 0.3);\n }\n\n .sticky-note:focus-within .drag-handle {\n max-width: 0px;\n padding-left: 0px;\n }\n\n /* Color picker */\n .color-picker {\n position: absolute;\n bottom: 4px;\n right: 4px;\n width: 8px;\n height: 8px;\n border: 1px solid rgba(0, 0, 0, 0.2);\n\n border-radius: 3px;\n background-color: var(--sticky-color);\n cursor: pointer;\n transition: transform 0.2s ease;\n }\n\n .color-picker:hover {\n transform: scale(1.1);\n }\n\n .color-options {\n position: absolute;\n bottom: 0;\n right: 0;\n display: flex;\n gap: 4px;\n background-color: rgba(255, 255, 255, 0.9);\n border: 1px solid #ccc;\n border-radius: 6px;\n padding: 3px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n transform-origin: bottom right;\n transform: scale(0);\n opacity: 0;\n transition: transform 0.2s ease, opacity 0.2s ease;\n z-index: 1000;\n }\n\n .color-options.expanded {\n transform: scale(1);\n opacity: 1;\n }\n\n .color-option {\n width: 12px;\n height: 12px;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 3px;\n cursor: pointer;\n transition: transform 0.15s ease, border-color 0.15s ease;\n }\n\n .color-option:hover {\n transform: scale(1.1);\n border-color: rgba(0, 0, 0, 0.4);\n }\n\n .color-option.yellow {\n background-color: #fef08a;\n }\n\n .color-option.blue {\n background-color: #bfdbfe;\n }\n\n .color-option.pink {\n background-color: #fce7f3;\n }\n\n .color-option.green {\n background-color: #d1fae5;\n }\n\n .color-option.gray {\n background-color: #f3f4f6;\n }\n `;\n }\n\n protected updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n if (changes.has('data') || changes.has('uuid')) {\n this.updateCanvasSize();\n }\n }\n\n private updateCanvasSize(): void {\n if (!this.data) {\n return;\n }\n\n const element = this.querySelector('.sticky-note');\n if (element) {\n const rect = element.getBoundingClientRect();\n getStore()\n .getState()\n .expandCanvas(\n this.data.position.left + rect.width,\n this.data.position.top + rect.height\n );\n }\n }\n\n private handleTitleBlur(event: FocusEvent): void {\n this.handleContentBlurForTouch(event);\n const target = event.target as HTMLElement;\n const newTitle = target.textContent || '';\n\n if (this.data && newTitle !== this.data.title) {\n getStore()\n .getState()\n .updateStickyNote(this.uuid, {\n ...this.data,\n title: newTitle\n });\n }\n this.requestUpdate();\n }\n\n private handleBodyBlur(event: FocusEvent): void {\n this.handleContentBlurForTouch(event);\n const target = event.target as HTMLElement;\n const newBody = target.innerText || '';\n\n if (this.data && newBody !== this.data.body) {\n getStore()\n .getState()\n .updateStickyNote(this.uuid, {\n ...this.data,\n body: newBody\n });\n }\n this.requestUpdate();\n }\n\n /* c8 ignore start -- touch-only handlers untestable in headless Chromium */\n private handleDragHandleTouchStart(event: TouchEvent): void {\n // Prevent Apple Pencil Scribble from activating on the adjacent\n // contenteditable fields when touching/dragging the handle.\n event.preventDefault();\n }\n\n /**\n * On touch devices, contenteditable is off by default. A tap on the\n * title or body enables it and focuses the element for editing.\n */\n private handleContentTap(event: TouchEvent): void {\n if (!this.isTouchDevice) return;\n const target = event.target as HTMLElement;\n if (\n !target.classList.contains('sticky-title') &&\n !target.classList.contains('sticky-body')\n )\n return;\n\n // Enable editing and focus\n target.setAttribute('contenteditable', 'true');\n this.editingField = target;\n target.focus();\n event.stopPropagation();\n }\n\n /**\n * When a contenteditable field loses focus on a touch device,\n * disable contenteditable again to prevent Scribble.\n */\n private handleContentBlurForTouch(event: FocusEvent): void {\n const target = event.target as HTMLElement;\n if (this.isTouchDevice && this.editingField === target) {\n target.setAttribute('contenteditable', 'false');\n this.editingField = null;\n }\n }\n /* c8 ignore stop */\n\n private handleContentMouseDown(event: MouseEvent): void {\n // If this sticky note is selected, don't stop propagation\n // so that group dragging can work\n if (this.selected) {\n return;\n }\n // Otherwise, stop propagation to enable editing\n event.stopPropagation();\n }\n\n private handleTitleKeyDown(event: KeyboardEvent): void {\n if (event.key === 'Enter') {\n event.preventDefault();\n (event.target as HTMLElement).blur();\n }\n if (event.key === 'Escape') {\n (event.target as HTMLElement).blur();\n }\n }\n\n private handleBodyKeyDown(event: KeyboardEvent): void {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n (event.target as HTMLElement).blur();\n }\n if (event.key === 'Escape') {\n (event.target as HTMLElement).blur();\n }\n }\n\n private handleColorPickerMouseEnter(): void {\n this.colorPickerExpanded = true;\n }\n\n private handleColorPickerMouseLeave(): void {\n this.colorPickerExpanded = false;\n }\n\n /* c8 ignore next 5 -- touch-only */\n private handleColorPickerTap(event: TouchEvent): void {\n event.stopPropagation();\n event.preventDefault();\n this.colorPickerExpanded = !this.colorPickerExpanded;\n }\n\n private handleColorOptionClick(\n event: MouseEvent | TouchEvent,\n color: 'yellow' | 'blue' | 'pink' | 'green' | 'gray'\n ): void {\n event.stopPropagation();\n event.preventDefault();\n\n if (this.data && color !== this.data.color) {\n getStore()\n .getState()\n .updateStickyNote(this.uuid, {\n ...this.data,\n color: color\n });\n }\n\n this.colorPickerExpanded = false;\n this.requestUpdate();\n }\n\n public render(): TemplateResult {\n if (!this.data) {\n return html`<div class=\"sticky-note\" style=\"display: none;\"></div>`;\n }\n\n const style = `left: ${this.data.position.left}px; top: ${this.data.position.top}px;`;\n\n return html`\n <div\n class=\"sticky-note ${this.data.color} ${this.dragging\n ? 'dragging'\n : ''}\"\n style=\"${style}\"\n data-uuid=\"${this.uuid}\"\n >\n <div class=\"sticky-title-container\">\n <temba-icon\n name=\"drag\"\n class=\"drag-handle\"\n @touchstart=${this.handleDragHandleTouchStart}\n ></temba-icon>\n <div\n class=\"sticky-title\"\n contenteditable=\"${!this.isTranslating && !this.isTouchDevice}\"\n @blur=\"${this.handleTitleBlur}\"\n @keydown=\"${this.handleTitleKeyDown}\"\n @mousedown=\"${this.handleContentMouseDown}\"\n @touchend=\"${this.handleContentTap}\"\n .textContent=\"${this.data.title}\"\n ></div>\n </div>\n <div class=\"sticky-body-container\">\n <div\n class=\"sticky-body\"\n contenteditable=\"${!this.isTranslating && !this.isTouchDevice}\"\n @blur=\"${this.handleBodyBlur}\"\n @keydown=\"${this.handleBodyKeyDown}\"\n @mousedown=\"${this.handleContentMouseDown}\"\n @touchend=\"${this.handleContentTap}\"\n .textContent=\"${this.data.body}\"\n ></div>\n ${!this.isTranslating\n ? html`<div class=\"edit-icon\" title=\"Edit note\"></div>`\n : ''}\n\n <!-- Color picker -->\n <div\n class=\"color-picker\"\n @mouseenter=\"${this.handleColorPickerMouseEnter}\"\n @mouseleave=\"${this.handleColorPickerMouseLeave}\"\n @touchend=\"${this.handleColorPickerTap}\"\n >\n <div\n class=\"color-options ${this.colorPickerExpanded\n ? 'expanded'\n : ''}\"\n >\n <div\n class=\"color-option yellow\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'yellow')}\"\n @touchend=\"${(e: TouchEvent) =>\n this.handleColorOptionClick(e, 'yellow')}\"\n ></div>\n <div\n class=\"color-option blue\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'blue')}\"\n @touchend=\"${(e: TouchEvent) =>\n this.handleColorOptionClick(e, 'blue')}\"\n ></div>\n <div\n class=\"color-option pink\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'pink')}\"\n @touchend=\"${(e: TouchEvent) =>\n this.handleColorOptionClick(e, 'pink')}\"\n ></div>\n <div\n class=\"color-option green\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'green')}\"\n @touchend=\"${(e: TouchEvent) =>\n this.handleColorOptionClick(e, 'green')}\"\n ></div>\n <div\n class=\"color-option gray\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'gray')}\"\n @touchend=\"${(e: TouchEvent) =>\n this.handleColorOptionClick(e, 'gray')}\"\n ></div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n}\n"]}
@@ -12,6 +12,17 @@ export const send_msg = {
12
12
  var _a;
13
13
  const text = action.text.replace(/\n/g, '<br>');
14
14
  return html `
15
+ ${action.template
16
+ ? html `<div
17
+ style="border: 1px solid #7dc8bc;padding: 0.5em;margin-bottom: 0.5em;border-radius: 4px; display:flex;align-items: flex-start;background: #f0faf7;color: #128C7E;font-size: 0.85em;"
18
+ >
19
+ <temba-icon
20
+ name="channel_wac"
21
+ style="--icon-size: 14px;"
22
+ ></temba-icon>
23
+ <div style="margin-left:0.4em">${action.template.name}</div>
24
+ </div>`
25
+ : null}
15
26
  ${renderClamped(html `${unsafeHTML(text)}`, action.text)}
16
27
  ${((_a = (action.quick_replies || [])) === null || _a === void 0 ? void 0 : _a.length) > 0
17
28
  ? html `<div class="quick-replies">
@@ -20,14 +31,6 @@ export const send_msg = {
20
31
  })}
21
32
  </div>`
22
33
  : null}
23
- ${action.template
24
- ? html `<div
25
- style="border: 1px solid var(--color-widget-border);padding: 0.5em;margin-top: 1em;border-radius: var(--curvature); display:flex;background: rgba(0,0,0,.03);"
26
- >
27
- <temba-icon name="channel_wac"></temba-icon>
28
- <div style="margin-left:0.5em">${action.template.name}</div>
29
- </div>`
30
- : null}
31
34
  `;
32
35
  },
33
36
  form: {
@@ -1 +1 @@
1
- {"version":3,"file":"send_msg.js","sourceRoot":"","sources":["../../../../src/flow/actions/send_msg.ts"],"names":[],"mappings":"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","sourcesContent":["import { html } from 'lit-html';\nimport { unsafeHTML } from 'lit-html/directives/unsafe-html.js';\nimport {\n ActionConfig,\n ACTION_GROUPS,\n FormData,\n ValidationResult,\n FlowTypes\n} from '../types';\nimport { Node, SendMsg } from '../../store/flow-definition';\nimport { titleCase } from '../../utils';\nimport { renderClamped } from '../utils';\n\nexport const send_msg: ActionConfig = {\n name: 'Send Message',\n group: ACTION_GROUPS.send,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n hideFromActions: true,\n render: (_node: Node, action: SendMsg) => {\n const text = action.text.replace(/\\n/g, '<br>');\n return html`\n ${renderClamped(html`${unsafeHTML(text)}`, action.text)}\n ${(action.quick_replies || [])?.length > 0\n ? html`<div class=\"quick-replies\">\n ${(action.quick_replies || []).map((reply) => {\n return html`<div class=\"quick-reply\">${reply}</div>`;\n })}\n </div>`\n : null}\n ${action.template\n ? html`<div\n style=\"border: 1px solid var(--color-widget-border);padding: 0.5em;margin-top: 1em;border-radius: var(--curvature); display:flex;background: rgba(0,0,0,.03);\"\n >\n <temba-icon name=\"channel_wac\"></temba-icon>\n <div style=\"margin-left:0.5em\">${action.template.name}</div>\n </div>`\n : null}\n `;\n },\n form: {\n text: {\n type: 'message-editor',\n label: 'Message',\n helpText:\n 'Enter the message to send with optional attachments. You can use expressions like @contact.name',\n required: true,\n evaluated: true,\n placeholder: 'Type your message here...',\n maxAttachments: 10,\n accept: '',\n endpoint: '/api/v2/media.json',\n counter: 'temba-charcount',\n gsm: true,\n autogrow: true\n },\n quick_replies: {\n type: 'select',\n options: [],\n multi: true,\n tags: true,\n searchable: true,\n placeholder: 'Add quick replies...',\n maxItems: 10,\n evaluated: true\n },\n template: {\n type: 'template-editor',\n endpoint: '/api/internal/templates.json'\n },\n runtime_attachments: {\n type: 'array',\n itemLabel: 'Attachment',\n sortable: true,\n maxItems: 10,\n isEmptyItem: (item: any) => {\n return !item.expression || item.expression.trim() === '';\n },\n itemConfig: {\n type: {\n type: 'select',\n width: '140px',\n options: [\n { value: 'image', name: 'Image' },\n { value: 'audio', name: 'Audio' },\n { value: 'video', name: 'Video' },\n { value: 'document', name: 'Document' }\n ],\n required: true,\n searchable: false\n },\n expression: {\n type: 'text',\n placeholder: 'Expression (e.g. @contact.photo)',\n required: true,\n evaluated: true\n }\n }\n }\n },\n layout: [\n 'text',\n {\n type: 'accordion',\n sections: [\n {\n label: 'Quick Replies',\n collapsed: true,\n getValueCount: (formData: FormData) => {\n return formData.quick_replies?.length || 0;\n },\n items: ['quick_replies']\n },\n {\n label: 'WhatsApp Template',\n collapsed: true,\n getValueCount: (formData: FormData) => {\n return !!formData.template;\n },\n items: ['template']\n },\n {\n label: 'Runtime Attachments',\n collapsed: true,\n getValueCount: (formData: FormData) => {\n return (\n formData.runtime_attachments?.filter(\n (item: any) =>\n item && item.expression && item.expression.trim() !== ''\n ).length || 0\n );\n },\n items: ['runtime_attachments']\n }\n ]\n }\n ],\n toFormData: (action: SendMsg) => {\n // Extract runtime attachments from the text field attachments\n const runtimeAttachments: {\n type: { name: string; value: string };\n expression: string;\n }[] = [];\n const staticAttachments: string[] = [];\n\n if (action.attachments && Array.isArray(action.attachments)) {\n action.attachments.forEach((attachment) => {\n if (typeof attachment === 'string' && attachment.includes(':')) {\n const colonIndex = attachment.indexOf(':');\n const contentType = attachment.substring(0, colonIndex);\n const value = attachment.substring(colonIndex + 1);\n\n if (!contentType.includes('/')) {\n runtimeAttachments.push({\n type: { name: titleCase(contentType), value: contentType },\n expression: value\n });\n } else {\n staticAttachments.push(attachment);\n }\n }\n });\n }\n\n return {\n uuid: action.uuid,\n text: action.text || '',\n attachments: staticAttachments,\n runtime_attachments: runtimeAttachments,\n quick_replies: (action.quick_replies || []).map((reply) => ({\n name: reply,\n value: reply\n })),\n template: action.template || null,\n template_variables: action.template_variables || []\n };\n },\n fromFormData: (data: FormData) => {\n const result = {\n uuid: data.uuid,\n type: 'send_msg',\n text: data.text || '',\n attachments: [],\n quick_replies: (data.quick_replies || []).map((reply: any) =>\n typeof reply === 'string' ? reply : reply.value || reply.name || reply\n )\n };\n\n // Combine static attachments from text field with runtime attachments\n const staticAttachments = data.attachments || [];\n const runtimeAttachments = (data.runtime_attachments || [])\n .filter(\n (item: {\n type: [{ name: string; value: string }];\n expression: string;\n }) => item && item.type && item.expression\n ) // Filter out invalid items\n .map(\n (item: {\n type: [{ name: string; value: string }];\n expression: string;\n }) => `${item.type[0].value}:${item.expression}`\n );\n\n result.attachments = [...staticAttachments, ...runtimeAttachments];\n\n // Remove quick_replies if empty to match original format\n if (result.quick_replies.length === 0) {\n delete (result as any).quick_replies;\n }\n\n // Add template and template_variables if a template is selected\n if (data.template) {\n (result as any).template = data.template;\n (result as any).template_variables = data.template_variables || [];\n }\n\n return result as SendMsg;\n },\n sanitize: (formData: FormData): void => {\n if (formData.text && typeof formData.text === 'string') {\n formData.text = formData.text.trim();\n }\n },\n validate: (formData: FormData): ValidationResult => {\n const errors: { [key: string]: string } = {};\n\n // Check total attachment count (static + runtime should not exceed 10)\n const staticAttachments = formData.attachments || [];\n const runtimeAttachments = (formData.runtime_attachments || []).filter(\n (item: any) => item && item.expression && item.expression.trim() !== ''\n );\n\n const totalAttachments =\n staticAttachments.length + runtimeAttachments.length;\n if (totalAttachments > 10) {\n if (runtimeAttachments.length > 0) {\n errors.runtime_attachments =\n 'Each message can only have up to 10 attachments';\n }\n if (staticAttachments.length > 0) {\n errors.text = 'Each message can only have up to 10 total attachments';\n }\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors\n };\n },\n localizable: ['text', 'quick_replies', 'attachments'],\n toLocalizationFormData: (\n action: SendMsg,\n localization: Record<string, any>\n ) => {\n // Convert localized values to form data format\n // Localized values are stored as arrays even for single values\n const formData: FormData = {\n uuid: action.uuid\n };\n\n // Handle text (single value, but stored as array in localization)\n if (localization.text && Array.isArray(localization.text)) {\n formData.text = localization.text[0] || '';\n } else {\n // Fall back to empty string if no localization\n formData.text = '';\n }\n\n // Handle attachments (already an array)\n if (localization.attachments && Array.isArray(localization.attachments)) {\n formData.attachments = localization.attachments;\n }\n\n // Handle quick_replies (already an array)\n if (\n localization.quick_replies &&\n Array.isArray(localization.quick_replies)\n ) {\n formData.quick_replies = localization.quick_replies.map((reply) => ({\n name: reply,\n value: reply\n }));\n }\n\n // Extract runtime attachments from localized attachments\n const runtimeAttachments: {\n type: { name: string; value: string };\n expression: string;\n }[] = [];\n const staticAttachments: string[] = [];\n\n if (formData.attachments && Array.isArray(formData.attachments)) {\n formData.attachments.forEach((attachment) => {\n if (typeof attachment === 'string' && attachment.includes(':')) {\n const colonIndex = attachment.indexOf(':');\n const contentType = attachment.substring(0, colonIndex);\n const value = attachment.substring(colonIndex + 1);\n\n if (!contentType.includes('/')) {\n runtimeAttachments.push({\n type: { name: titleCase(contentType), value: contentType },\n expression: value\n });\n } else {\n staticAttachments.push(attachment);\n }\n }\n });\n }\n\n formData.attachments = staticAttachments;\n formData.runtime_attachments = runtimeAttachments;\n\n return formData;\n },\n fromLocalizationFormData: (formData: FormData, action: SendMsg) => {\n // Convert form data to localization format\n // All values must be stored as arrays\n const localization: Record<string, any> = {};\n\n // Handle text (store as single-element array)\n // Only save if not empty and different from base action\n if (formData.text && formData.text.trim() !== '') {\n if (formData.text !== action.text) {\n localization.text = [formData.text];\n }\n }\n\n // Handle quick_replies (store as array)\n const quickReplies = (formData.quick_replies || [])\n .map((reply: any) =>\n typeof reply === 'string' ? reply : reply.value || reply.name || reply\n )\n .filter((reply: string) => reply && reply.trim() !== '');\n\n // Only save if there are quick replies and different from base action\n if (quickReplies.length > 0) {\n if (\n JSON.stringify(quickReplies) !==\n JSON.stringify(action.quick_replies || [])\n ) {\n localization.quick_replies = quickReplies;\n }\n }\n\n // Handle attachments (combine static and runtime attachments)\n const staticAttachments = (formData.attachments || []).filter(\n (att: string) => att && att.trim() !== ''\n );\n const runtimeAttachments = (formData.runtime_attachments || [])\n .filter(\n (item: {\n type: [{ name: string; value: string }];\n expression: string;\n }) =>\n item && item.type && item.expression && item.expression.trim() !== ''\n )\n .map(\n (item: {\n type: [{ name: string; value: string }];\n expression: string;\n }) => `${item.type[0].value}:${item.expression}`\n );\n\n const allAttachments = [...staticAttachments, ...runtimeAttachments];\n\n // Only save if there are attachments and different from base action\n if (allAttachments.length > 0) {\n if (\n JSON.stringify(allAttachments) !==\n JSON.stringify(action.attachments || [])\n ) {\n localization.attachments = allAttachments;\n }\n }\n\n return localization;\n }\n};\n"]}
1
+ {"version":3,"file":"send_msg.js","sourceRoot":"","sources":["../../../../src/flow/actions/send_msg.ts"],"names":[],"mappings":"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","sourcesContent":["import { html } from 'lit-html';\nimport { unsafeHTML } from 'lit-html/directives/unsafe-html.js';\nimport {\n ActionConfig,\n ACTION_GROUPS,\n FormData,\n ValidationResult,\n FlowTypes\n} from '../types';\nimport { Node, SendMsg } from '../../store/flow-definition';\nimport { titleCase } from '../../utils';\nimport { renderClamped } from '../utils';\n\nexport const send_msg: ActionConfig = {\n name: 'Send Message',\n group: ACTION_GROUPS.send,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n hideFromActions: true,\n render: (_node: Node, action: SendMsg) => {\n const text = action.text.replace(/\\n/g, '<br>');\n return html`\n ${action.template\n ? html`<div\n style=\"border: 1px solid #7dc8bc;padding: 0.5em;margin-bottom: 0.5em;border-radius: 4px; display:flex;align-items: flex-start;background: #f0faf7;color: #128C7E;font-size: 0.85em;\"\n >\n <temba-icon\n name=\"channel_wac\"\n style=\"--icon-size: 14px;\"\n ></temba-icon>\n <div style=\"margin-left:0.4em\">${action.template.name}</div>\n </div>`\n : null}\n ${renderClamped(html`${unsafeHTML(text)}`, action.text)}\n ${(action.quick_replies || [])?.length > 0\n ? html`<div class=\"quick-replies\">\n ${(action.quick_replies || []).map((reply) => {\n return html`<div class=\"quick-reply\">${reply}</div>`;\n })}\n </div>`\n : null}\n `;\n },\n form: {\n text: {\n type: 'message-editor',\n label: 'Message',\n helpText:\n 'Enter the message to send with optional attachments. You can use expressions like @contact.name',\n required: true,\n evaluated: true,\n placeholder: 'Type your message here...',\n maxAttachments: 10,\n accept: '',\n endpoint: '/api/v2/media.json',\n counter: 'temba-charcount',\n gsm: true,\n autogrow: true\n },\n quick_replies: {\n type: 'select',\n options: [],\n multi: true,\n tags: true,\n searchable: true,\n placeholder: 'Add quick replies...',\n maxItems: 10,\n evaluated: true\n },\n template: {\n type: 'template-editor',\n endpoint: '/api/internal/templates.json'\n },\n runtime_attachments: {\n type: 'array',\n itemLabel: 'Attachment',\n sortable: true,\n maxItems: 10,\n isEmptyItem: (item: any) => {\n return !item.expression || item.expression.trim() === '';\n },\n itemConfig: {\n type: {\n type: 'select',\n width: '140px',\n options: [\n { value: 'image', name: 'Image' },\n { value: 'audio', name: 'Audio' },\n { value: 'video', name: 'Video' },\n { value: 'document', name: 'Document' }\n ],\n required: true,\n searchable: false\n },\n expression: {\n type: 'text',\n placeholder: 'Expression (e.g. @contact.photo)',\n required: true,\n evaluated: true\n }\n }\n }\n },\n layout: [\n 'text',\n {\n type: 'accordion',\n sections: [\n {\n label: 'Quick Replies',\n collapsed: true,\n getValueCount: (formData: FormData) => {\n return formData.quick_replies?.length || 0;\n },\n items: ['quick_replies']\n },\n {\n label: 'WhatsApp Template',\n collapsed: true,\n getValueCount: (formData: FormData) => {\n return !!formData.template;\n },\n items: ['template']\n },\n {\n label: 'Runtime Attachments',\n collapsed: true,\n getValueCount: (formData: FormData) => {\n return (\n formData.runtime_attachments?.filter(\n (item: any) =>\n item && item.expression && item.expression.trim() !== ''\n ).length || 0\n );\n },\n items: ['runtime_attachments']\n }\n ]\n }\n ],\n toFormData: (action: SendMsg) => {\n // Extract runtime attachments from the text field attachments\n const runtimeAttachments: {\n type: { name: string; value: string };\n expression: string;\n }[] = [];\n const staticAttachments: string[] = [];\n\n if (action.attachments && Array.isArray(action.attachments)) {\n action.attachments.forEach((attachment) => {\n if (typeof attachment === 'string' && attachment.includes(':')) {\n const colonIndex = attachment.indexOf(':');\n const contentType = attachment.substring(0, colonIndex);\n const value = attachment.substring(colonIndex + 1);\n\n if (!contentType.includes('/')) {\n runtimeAttachments.push({\n type: { name: titleCase(contentType), value: contentType },\n expression: value\n });\n } else {\n staticAttachments.push(attachment);\n }\n }\n });\n }\n\n return {\n uuid: action.uuid,\n text: action.text || '',\n attachments: staticAttachments,\n runtime_attachments: runtimeAttachments,\n quick_replies: (action.quick_replies || []).map((reply) => ({\n name: reply,\n value: reply\n })),\n template: action.template || null,\n template_variables: action.template_variables || []\n };\n },\n fromFormData: (data: FormData) => {\n const result = {\n uuid: data.uuid,\n type: 'send_msg',\n text: data.text || '',\n attachments: [],\n quick_replies: (data.quick_replies || []).map((reply: any) =>\n typeof reply === 'string' ? reply : reply.value || reply.name || reply\n )\n };\n\n // Combine static attachments from text field with runtime attachments\n const staticAttachments = data.attachments || [];\n const runtimeAttachments = (data.runtime_attachments || [])\n .filter(\n (item: {\n type: [{ name: string; value: string }];\n expression: string;\n }) => item && item.type && item.expression\n ) // Filter out invalid items\n .map(\n (item: {\n type: [{ name: string; value: string }];\n expression: string;\n }) => `${item.type[0].value}:${item.expression}`\n );\n\n result.attachments = [...staticAttachments, ...runtimeAttachments];\n\n // Remove quick_replies if empty to match original format\n if (result.quick_replies.length === 0) {\n delete (result as any).quick_replies;\n }\n\n // Add template and template_variables if a template is selected\n if (data.template) {\n (result as any).template = data.template;\n (result as any).template_variables = data.template_variables || [];\n }\n\n return result as SendMsg;\n },\n sanitize: (formData: FormData): void => {\n if (formData.text && typeof formData.text === 'string') {\n formData.text = formData.text.trim();\n }\n },\n validate: (formData: FormData): ValidationResult => {\n const errors: { [key: string]: string } = {};\n\n // Check total attachment count (static + runtime should not exceed 10)\n const staticAttachments = formData.attachments || [];\n const runtimeAttachments = (formData.runtime_attachments || []).filter(\n (item: any) => item && item.expression && item.expression.trim() !== ''\n );\n\n const totalAttachments =\n staticAttachments.length + runtimeAttachments.length;\n if (totalAttachments > 10) {\n if (runtimeAttachments.length > 0) {\n errors.runtime_attachments =\n 'Each message can only have up to 10 attachments';\n }\n if (staticAttachments.length > 0) {\n errors.text = 'Each message can only have up to 10 total attachments';\n }\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors\n };\n },\n localizable: ['text', 'quick_replies', 'attachments'],\n toLocalizationFormData: (\n action: SendMsg,\n localization: Record<string, any>\n ) => {\n // Convert localized values to form data format\n // Localized values are stored as arrays even for single values\n const formData: FormData = {\n uuid: action.uuid\n };\n\n // Handle text (single value, but stored as array in localization)\n if (localization.text && Array.isArray(localization.text)) {\n formData.text = localization.text[0] || '';\n } else {\n // Fall back to empty string if no localization\n formData.text = '';\n }\n\n // Handle attachments (already an array)\n if (localization.attachments && Array.isArray(localization.attachments)) {\n formData.attachments = localization.attachments;\n }\n\n // Handle quick_replies (already an array)\n if (\n localization.quick_replies &&\n Array.isArray(localization.quick_replies)\n ) {\n formData.quick_replies = localization.quick_replies.map((reply) => ({\n name: reply,\n value: reply\n }));\n }\n\n // Extract runtime attachments from localized attachments\n const runtimeAttachments: {\n type: { name: string; value: string };\n expression: string;\n }[] = [];\n const staticAttachments: string[] = [];\n\n if (formData.attachments && Array.isArray(formData.attachments)) {\n formData.attachments.forEach((attachment) => {\n if (typeof attachment === 'string' && attachment.includes(':')) {\n const colonIndex = attachment.indexOf(':');\n const contentType = attachment.substring(0, colonIndex);\n const value = attachment.substring(colonIndex + 1);\n\n if (!contentType.includes('/')) {\n runtimeAttachments.push({\n type: { name: titleCase(contentType), value: contentType },\n expression: value\n });\n } else {\n staticAttachments.push(attachment);\n }\n }\n });\n }\n\n formData.attachments = staticAttachments;\n formData.runtime_attachments = runtimeAttachments;\n\n return formData;\n },\n fromLocalizationFormData: (formData: FormData, action: SendMsg) => {\n // Convert form data to localization format\n // All values must be stored as arrays\n const localization: Record<string, any> = {};\n\n // Handle text (store as single-element array)\n // Only save if not empty and different from base action\n if (formData.text && formData.text.trim() !== '') {\n if (formData.text !== action.text) {\n localization.text = [formData.text];\n }\n }\n\n // Handle quick_replies (store as array)\n const quickReplies = (formData.quick_replies || [])\n .map((reply: any) =>\n typeof reply === 'string' ? reply : reply.value || reply.name || reply\n )\n .filter((reply: string) => reply && reply.trim() !== '');\n\n // Only save if there are quick replies and different from base action\n if (quickReplies.length > 0) {\n if (\n JSON.stringify(quickReplies) !==\n JSON.stringify(action.quick_replies || [])\n ) {\n localization.quick_replies = quickReplies;\n }\n }\n\n // Handle attachments (combine static and runtime attachments)\n const staticAttachments = (formData.attachments || []).filter(\n (att: string) => att && att.trim() !== ''\n );\n const runtimeAttachments = (formData.runtime_attachments || [])\n .filter(\n (item: {\n type: [{ name: string; value: string }];\n expression: string;\n }) =>\n item && item.type && item.expression && item.expression.trim() !== ''\n )\n .map(\n (item: {\n type: [{ name: string; value: string }];\n expression: string;\n }) => `${item.type[0].value}:${item.expression}`\n );\n\n const allAttachments = [...staticAttachments, ...runtimeAttachments];\n\n // Only save if there are attachments and different from base action\n if (allAttachments.length > 0) {\n if (\n JSON.stringify(allAttachments) !==\n JSON.stringify(action.attachments || [])\n ) {\n localization.attachments = allAttachments;\n }\n }\n\n return localization;\n }\n};\n"]}
@@ -26,7 +26,9 @@ export const split_by_subflow = {
26
26
  const enterFlowAction = (_a = node.actions) === null || _a === void 0 ? void 0 : _a.find((action) => action.type === 'enter_flow');
27
27
  return html `
28
28
  <div class="body">
29
- ${(enterFlowAction === null || enterFlowAction === void 0 ? void 0 : enterFlowAction.flow) ? renderFlowLinks([enterFlowAction.flow], 'flow') : null}
29
+ ${(enterFlowAction === null || enterFlowAction === void 0 ? void 0 : enterFlowAction.flow)
30
+ ? renderFlowLinks([enterFlowAction.flow], 'flow')
31
+ : null}
30
32
  </div>
31
33
  `;
32
34
  },
@@ -1 +1 @@
1
- {"version":3,"file":"split_by_subflow.js","sourceRoot":"","sources":["../../../../src/flow/nodes/split_by_subflow.ts"],"names":[],"mappings":"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","sourcesContent":["import { ACTION_GROUPS, FormData, NodeConfig, FlowTypes } from '../types';\nimport { Node } from '../../store/flow-definition';\nimport { generateUUID } from '../../utils';\nimport { html } from 'lit';\nimport { renderFlowLinks } from '../utils';\nimport {\n categoriesToLocalizationFormData,\n localizationFormDataToCategories\n} from './shared';\n\nexport const split_by_subflow: NodeConfig = {\n type: 'split_by_subflow',\n name: 'Enter a Flow',\n group: ACTION_GROUPS.trigger,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n showAsAction: true,\n form: {\n flow: {\n type: 'select',\n required: true,\n placeholder: 'Select a flow...',\n helpText:\n 'Once the subflow is complete or expires, the contact will return here',\n endpoint: '/api/v2/flows.json',\n valueKey: 'uuid',\n nameKey: 'name'\n }\n },\n layout: ['flow'],\n render: (node: Node) => {\n const enterFlowAction = node.actions?.find(\n (action) => action.type === 'enter_flow'\n ) as any;\n return html`\n <div class=\"body\">\n ${enterFlowAction?.flow ? renderFlowLinks([enterFlowAction.flow], 'flow') : null}\n </div>\n `;\n },\n toFormData: (node: Node) => {\n // Extract data from the existing node structure\n const enterFlowAction = node.actions?.find(\n (action) => action.type === 'enter_flow'\n ) as any;\n\n return {\n uuid: node.uuid,\n flow: enterFlowAction?.flow\n ? [{ uuid: enterFlowAction.flow.uuid, name: enterFlowAction.flow.name }]\n : []\n };\n },\n fromFormData: (formData: FormData, originalNode: Node): Node => {\n // Get flow selection\n const flowSelection =\n Array.isArray(formData.flow) && formData.flow.length > 0\n ? formData.flow[0]\n : null;\n\n // Find existing enter_flow action to preserve its UUID\n const existingEnterFlowAction = originalNode.actions?.find(\n (action) => action.type === 'enter_flow'\n );\n const enterFlowUuid = existingEnterFlowAction?.uuid || generateUUID();\n\n // Create enter_flow action\n const enterFlowAction: any = {\n type: 'enter_flow',\n uuid: enterFlowUuid,\n flow: flowSelection\n ? {\n uuid: flowSelection.uuid || flowSelection.value,\n name: flowSelection.name\n }\n : { uuid: '', name: '' }\n };\n\n // Create categories and exits for Complete and Expired\n const existingCategories = originalNode.router?.categories || [];\n const existingExits = originalNode.exits || [];\n const existingCases = originalNode.router?.cases || [];\n\n // Find existing Complete category\n const existingCompleteCategory = existingCategories.find(\n (cat) => cat.name === 'Complete'\n );\n const existingCompleteExit = existingCompleteCategory\n ? existingExits.find(\n (exit) => exit.uuid === existingCompleteCategory.exit_uuid\n )\n : null;\n const existingCompleteCase = existingCompleteCategory\n ? existingCases.find(\n (case_) => case_.category_uuid === existingCompleteCategory.uuid\n )\n : null;\n\n const completeCategoryUuid =\n existingCompleteCategory?.uuid || generateUUID();\n const completeExitUuid = existingCompleteExit?.uuid || generateUUID();\n const completeCaseUuid = existingCompleteCase?.uuid || generateUUID();\n\n // Find existing Expired category\n const existingExpiredCategory = existingCategories.find(\n (cat) => cat.name === 'Expired'\n );\n const existingExpiredExit = existingExpiredCategory\n ? existingExits.find(\n (exit) => exit.uuid === existingExpiredCategory.exit_uuid\n )\n : null;\n\n const expiredCategoryUuid = existingExpiredCategory?.uuid || generateUUID();\n const expiredExitUuid = existingExpiredExit?.uuid || generateUUID();\n\n const categories = [\n {\n uuid: completeCategoryUuid,\n name: 'Complete',\n exit_uuid: completeExitUuid\n },\n {\n uuid: expiredCategoryUuid,\n name: 'Expired',\n exit_uuid: expiredExitUuid\n }\n ];\n\n const exits = [\n {\n uuid: completeExitUuid,\n destination_uuid: existingCompleteExit?.destination_uuid || null\n },\n {\n uuid: expiredExitUuid,\n destination_uuid: existingExpiredExit?.destination_uuid || null\n }\n ];\n\n const cases = [\n {\n uuid: completeCaseUuid,\n type: 'has_only_text',\n arguments: ['completed'],\n category_uuid: completeCategoryUuid\n }\n ];\n\n // Create the router\n const router = {\n type: 'switch' as const,\n categories: categories,\n default_category_uuid: expiredCategoryUuid,\n operand: '@child.status',\n cases: cases\n };\n\n // Return the complete node\n return {\n uuid: originalNode.uuid,\n actions: [enterFlowAction],\n router: router,\n exits: exits\n };\n },\n\n // Localization support for categories\n localizable: 'categories',\n toLocalizationFormData: categoriesToLocalizationFormData,\n fromLocalizationFormData: localizationFormDataToCategories\n};\n"]}
1
+ {"version":3,"file":"split_by_subflow.js","sourceRoot":"","sources":["../../../../src/flow/nodes/split_by_subflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAwB,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EACL,gCAAgC,EAChC,gCAAgC,EACjC,MAAM,UAAU,CAAC;AAElB,MAAM,CAAC,MAAM,gBAAgB,GAAe;IAC1C,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE,cAAc;IACpB,KAAK,EAAE,aAAa,CAAC,OAAO;IAC5B,SAAS,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC;IACrE,YAAY,EAAE,IAAI;IAClB,IAAI,EAAE;QACJ,IAAI,EAAE;YACJ,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,IAAI;YACd,WAAW,EAAE,kBAAkB;YAC/B,QAAQ,EACN,uEAAuE;YACzE,QAAQ,EAAE,oBAAoB;YAC9B,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,MAAM;SAChB;KACF;IACD,MAAM,EAAE,CAAC,MAAM,CAAC;IAChB,MAAM,EAAE,CAAC,IAAU,EAAE,EAAE;;QACrB,MAAM,eAAe,GAAG,MAAA,IAAI,CAAC,OAAO,0CAAE,IAAI,CACxC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY,CAClC,CAAC;QACT,OAAO,IAAI,CAAA;;UAEL,CAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,IAAI;YACrB,CAAC,CAAC,eAAe,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YACjD,CAAC,CAAC,IAAI;;KAEX,CAAC;IACJ,CAAC;IACD,UAAU,EAAE,CAAC,IAAU,EAAE,EAAE;;QACzB,gDAAgD;QAChD,MAAM,eAAe,GAAG,MAAA,IAAI,CAAC,OAAO,0CAAE,IAAI,CACxC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY,CAClC,CAAC;QAET,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,CAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,IAAI;gBACzB,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACxE,CAAC,CAAC,EAAE;SACP,CAAC;IACJ,CAAC;IACD,YAAY,EAAE,CAAC,QAAkB,EAAE,YAAkB,EAAQ,EAAE;;QAC7D,qBAAqB;QACrB,MAAM,aAAa,GACjB,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;YACtD,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;YAClB,CAAC,CAAC,IAAI,CAAC;QAEX,uDAAuD;QACvD,MAAM,uBAAuB,GAAG,MAAA,YAAY,CAAC,OAAO,0CAAE,IAAI,CACxD,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY,CACzC,CAAC;QACF,MAAM,aAAa,GAAG,CAAA,uBAAuB,aAAvB,uBAAuB,uBAAvB,uBAAuB,CAAE,IAAI,KAAI,YAAY,EAAE,CAAC;QAEtE,2BAA2B;QAC3B,MAAM,eAAe,GAAQ;YAC3B,IAAI,EAAE,YAAY;YAClB,IAAI,EAAE,aAAa;YACnB,IAAI,EAAE,aAAa;gBACjB,CAAC,CAAC;oBACE,IAAI,EAAE,aAAa,CAAC,IAAI,IAAI,aAAa,CAAC,KAAK;oBAC/C,IAAI,EAAE,aAAa,CAAC,IAAI;iBACzB;gBACH,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;SAC3B,CAAC;QAEF,uDAAuD;QACvD,MAAM,kBAAkB,GAAG,CAAA,MAAA,YAAY,CAAC,MAAM,0CAAE,UAAU,KAAI,EAAE,CAAC;QACjE,MAAM,aAAa,GAAG,YAAY,CAAC,KAAK,IAAI,EAAE,CAAC;QAC/C,MAAM,aAAa,GAAG,CAAA,MAAA,YAAY,CAAC,MAAM,0CAAE,KAAK,KAAI,EAAE,CAAC;QAEvD,kCAAkC;QAClC,MAAM,wBAAwB,GAAG,kBAAkB,CAAC,IAAI,CACtD,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,UAAU,CACjC,CAAC;QACF,MAAM,oBAAoB,GAAG,wBAAwB;YACnD,CAAC,CAAC,aAAa,CAAC,IAAI,CAChB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,wBAAwB,CAAC,SAAS,CAC3D;YACH,CAAC,CAAC,IAAI,CAAC;QACT,MAAM,oBAAoB,GAAG,wBAAwB;YACnD,CAAC,CAAC,aAAa,CAAC,IAAI,CAChB,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,aAAa,KAAK,wBAAwB,CAAC,IAAI,CACjE;YACH,CAAC,CAAC,IAAI,CAAC;QAET,MAAM,oBAAoB,GACxB,CAAA,wBAAwB,aAAxB,wBAAwB,uBAAxB,wBAAwB,CAAE,IAAI,KAAI,YAAY,EAAE,CAAC;QACnD,MAAM,gBAAgB,GAAG,CAAA,oBAAoB,aAApB,oBAAoB,uBAApB,oBAAoB,CAAE,IAAI,KAAI,YAAY,EAAE,CAAC;QACtE,MAAM,gBAAgB,GAAG,CAAA,oBAAoB,aAApB,oBAAoB,uBAApB,oBAAoB,CAAE,IAAI,KAAI,YAAY,EAAE,CAAC;QAEtE,iCAAiC;QACjC,MAAM,uBAAuB,GAAG,kBAAkB,CAAC,IAAI,CACrD,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,SAAS,CAChC,CAAC;QACF,MAAM,mBAAmB,GAAG,uBAAuB;YACjD,CAAC,CAAC,aAAa,CAAC,IAAI,CAChB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,uBAAuB,CAAC,SAAS,CAC1D;YACH,CAAC,CAAC,IAAI,CAAC;QAET,MAAM,mBAAmB,GAAG,CAAA,uBAAuB,aAAvB,uBAAuB,uBAAvB,uBAAuB,CAAE,IAAI,KAAI,YAAY,EAAE,CAAC;QAC5E,MAAM,eAAe,GAAG,CAAA,mBAAmB,aAAnB,mBAAmB,uBAAnB,mBAAmB,CAAE,IAAI,KAAI,YAAY,EAAE,CAAC;QAEpE,MAAM,UAAU,GAAG;YACjB;gBACE,IAAI,EAAE,oBAAoB;gBAC1B,IAAI,EAAE,UAAU;gBAChB,SAAS,EAAE,gBAAgB;aAC5B;YACD;gBACE,IAAI,EAAE,mBAAmB;gBACzB,IAAI,EAAE,SAAS;gBACf,SAAS,EAAE,eAAe;aAC3B;SACF,CAAC;QAEF,MAAM,KAAK,GAAG;YACZ;gBACE,IAAI,EAAE,gBAAgB;gBACtB,gBAAgB,EAAE,CAAA,oBAAoB,aAApB,oBAAoB,uBAApB,oBAAoB,CAAE,gBAAgB,KAAI,IAAI;aACjE;YACD;gBACE,IAAI,EAAE,eAAe;gBACrB,gBAAgB,EAAE,CAAA,mBAAmB,aAAnB,mBAAmB,uBAAnB,mBAAmB,CAAE,gBAAgB,KAAI,IAAI;aAChE;SACF,CAAC;QAEF,MAAM,KAAK,GAAG;YACZ;gBACE,IAAI,EAAE,gBAAgB;gBACtB,IAAI,EAAE,eAAe;gBACrB,SAAS,EAAE,CAAC,WAAW,CAAC;gBACxB,aAAa,EAAE,oBAAoB;aACpC;SACF,CAAC;QAEF,oBAAoB;QACpB,MAAM,MAAM,GAAG;YACb,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE,UAAU;YACtB,qBAAqB,EAAE,mBAAmB;YAC1C,OAAO,EAAE,eAAe;YACxB,KAAK,EAAE,KAAK;SACb,CAAC;QAEF,2BAA2B;QAC3B,OAAO;YACL,IAAI,EAAE,YAAY,CAAC,IAAI;YACvB,OAAO,EAAE,CAAC,eAAe,CAAC;YAC1B,MAAM,EAAE,MAAM;YACd,KAAK,EAAE,KAAK;SACb,CAAC;IACJ,CAAC;IAED,sCAAsC;IACtC,WAAW,EAAE,YAAY;IACzB,sBAAsB,EAAE,gCAAgC;IACxD,wBAAwB,EAAE,gCAAgC;CAC3D,CAAC","sourcesContent":["import { ACTION_GROUPS, FormData, NodeConfig, FlowTypes } from '../types';\nimport { Node } from '../../store/flow-definition';\nimport { generateUUID } from '../../utils';\nimport { html } from 'lit';\nimport { renderFlowLinks } from '../utils';\nimport {\n categoriesToLocalizationFormData,\n localizationFormDataToCategories\n} from './shared';\n\nexport const split_by_subflow: NodeConfig = {\n type: 'split_by_subflow',\n name: 'Enter a Flow',\n group: ACTION_GROUPS.trigger,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n showAsAction: true,\n form: {\n flow: {\n type: 'select',\n required: true,\n placeholder: 'Select a flow...',\n helpText:\n 'Once the subflow is complete or expires, the contact will return here',\n endpoint: '/api/v2/flows.json',\n valueKey: 'uuid',\n nameKey: 'name'\n }\n },\n layout: ['flow'],\n render: (node: Node) => {\n const enterFlowAction = node.actions?.find(\n (action) => action.type === 'enter_flow'\n ) as any;\n return html`\n <div class=\"body\">\n ${enterFlowAction?.flow\n ? renderFlowLinks([enterFlowAction.flow], 'flow')\n : null}\n </div>\n `;\n },\n toFormData: (node: Node) => {\n // Extract data from the existing node structure\n const enterFlowAction = node.actions?.find(\n (action) => action.type === 'enter_flow'\n ) as any;\n\n return {\n uuid: node.uuid,\n flow: enterFlowAction?.flow\n ? [{ uuid: enterFlowAction.flow.uuid, name: enterFlowAction.flow.name }]\n : []\n };\n },\n fromFormData: (formData: FormData, originalNode: Node): Node => {\n // Get flow selection\n const flowSelection =\n Array.isArray(formData.flow) && formData.flow.length > 0\n ? formData.flow[0]\n : null;\n\n // Find existing enter_flow action to preserve its UUID\n const existingEnterFlowAction = originalNode.actions?.find(\n (action) => action.type === 'enter_flow'\n );\n const enterFlowUuid = existingEnterFlowAction?.uuid || generateUUID();\n\n // Create enter_flow action\n const enterFlowAction: any = {\n type: 'enter_flow',\n uuid: enterFlowUuid,\n flow: flowSelection\n ? {\n uuid: flowSelection.uuid || flowSelection.value,\n name: flowSelection.name\n }\n : { uuid: '', name: '' }\n };\n\n // Create categories and exits for Complete and Expired\n const existingCategories = originalNode.router?.categories || [];\n const existingExits = originalNode.exits || [];\n const existingCases = originalNode.router?.cases || [];\n\n // Find existing Complete category\n const existingCompleteCategory = existingCategories.find(\n (cat) => cat.name === 'Complete'\n );\n const existingCompleteExit = existingCompleteCategory\n ? existingExits.find(\n (exit) => exit.uuid === existingCompleteCategory.exit_uuid\n )\n : null;\n const existingCompleteCase = existingCompleteCategory\n ? existingCases.find(\n (case_) => case_.category_uuid === existingCompleteCategory.uuid\n )\n : null;\n\n const completeCategoryUuid =\n existingCompleteCategory?.uuid || generateUUID();\n const completeExitUuid = existingCompleteExit?.uuid || generateUUID();\n const completeCaseUuid = existingCompleteCase?.uuid || generateUUID();\n\n // Find existing Expired category\n const existingExpiredCategory = existingCategories.find(\n (cat) => cat.name === 'Expired'\n );\n const existingExpiredExit = existingExpiredCategory\n ? existingExits.find(\n (exit) => exit.uuid === existingExpiredCategory.exit_uuid\n )\n : null;\n\n const expiredCategoryUuid = existingExpiredCategory?.uuid || generateUUID();\n const expiredExitUuid = existingExpiredExit?.uuid || generateUUID();\n\n const categories = [\n {\n uuid: completeCategoryUuid,\n name: 'Complete',\n exit_uuid: completeExitUuid\n },\n {\n uuid: expiredCategoryUuid,\n name: 'Expired',\n exit_uuid: expiredExitUuid\n }\n ];\n\n const exits = [\n {\n uuid: completeExitUuid,\n destination_uuid: existingCompleteExit?.destination_uuid || null\n },\n {\n uuid: expiredExitUuid,\n destination_uuid: existingExpiredExit?.destination_uuid || null\n }\n ];\n\n const cases = [\n {\n uuid: completeCaseUuid,\n type: 'has_only_text',\n arguments: ['completed'],\n category_uuid: completeCategoryUuid\n }\n ];\n\n // Create the router\n const router = {\n type: 'switch' as const,\n categories: categories,\n default_category_uuid: expiredCategoryUuid,\n operand: '@child.status',\n cases: cases\n };\n\n // Return the complete node\n return {\n uuid: originalNode.uuid,\n actions: [enterFlowAction],\n router: router,\n exits: exits\n };\n },\n\n // Localization support for categories\n localizable: 'categories',\n toLocalizationFormData: categoriesToLocalizationFormData,\n fromLocalizationFormData: localizationFormDataToCategories\n};\n"]}
@@ -134,9 +134,7 @@ export const renderFlowLinks = (flows, icon) => {
134
134
  itemElements.push(html `<div
135
135
  style="display:flex;items-align:center;margin-top:0.2em;"
136
136
  >
137
- ${icon
138
- ? html `<div style="margin-right:0.4em; width: 1em;"></div>`
139
- : null}
137
+ ${icon ? html `<div style="margin-right:0.4em; width: 1em;"></div>` : null}
140
138
  <div style="font-size:0.8em">+${remainingCount} more</div>
141
139
  </div>`);
142
140
  }
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/flow/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, TemplateResult } from 'lit-html';\nimport { NamedObject, FlowPosition } from '../store/flow-definition';\nimport { FlowIssue } from '../store/AppState';\nimport { CustomEventType } from '../interfaces';\n\nconst IS_MAC =\n typeof navigator !== 'undefined' &&\n /Mac|iPod|iPhone|iPad/.test(navigator.platform);\n\n/**\n * Returns true if the mouse event is a right-click or equivalent:\n * - button !== 0 (actual right-click or middle-click on any platform)\n * - ctrl+click on macOS (emulates right-click / context menu)\n */\nexport function isRightClick(event: MouseEvent): boolean {\n if (event.button !== 0) return true;\n if (IS_MAC && event.ctrlKey) return true;\n return false;\n}\n\nexport function formatIssueMessage(issue: FlowIssue): string {\n if (issue.dependency) {\n const name = issue.dependency.name || issue.dependency.key;\n return `Cannot find a ${issue.dependency.type} for ${name}`;\n }\n return issue.description;\n}\n\nconst GRID_SIZE = 20;\n\nexport function snapToGrid(value: number): number {\n const snapped = Math.round(value / GRID_SIZE) * GRID_SIZE;\n return Math.max(snapped, 0);\n}\n\n/**\n * Renders content clamped to a maximum number of lines with ellipsis.\n * Hovering shows the full text in a tooltip.\n */\nexport const renderClamped = (\n content: TemplateResult | string,\n titleText: string,\n maxLines: number = 3\n) => {\n return html`<div\n style=\"display: -webkit-box; -webkit-line-clamp: ${maxLines}; -webkit-box-orient: vertical; overflow: hidden; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto;\"\n title=\"${titleText}\"\n >\n ${content}\n </div>`;\n};\n\n/**\n * Renders a single line item with optional icon\n */\nexport const renderLineItem = (name: string, icon?: string) => {\n return html`<div style=\"display:flex;items-align:center;\">\n ${icon\n ? html`<temba-icon name=${icon} style=\"margin-right:0.5em\"></temba-icon>`\n : null}\n <div\n style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 250px;\"\n title=\"${name}\"\n >\n ${name}\n </div>\n </div>`;\n};\n\n/**\n * Renders a list of named objects with optional icon, showing up to 3 items\n * with a \"+X more\" indicator if there are more items\n */\nexport const renderNamedObjects = (assets: NamedObject[], icon?: string) => {\n return renderStringList(\n assets.map((asset) => asset.name),\n icon\n );\n};\n\n/**\n * Renders a list of strings with optional icon, showing up to 3 items\n * with a \"+X more\" indicator if there are more items\n */\nexport const renderStringList = (items: string[], icon?: string) => {\n const itemElements = [];\n const maxDisplay = 3;\n\n // Show up to 3 items, or all 4 if exactly 4 items\n const displayCount =\n items.length === 4 ? 4 : Math.min(maxDisplay, items.length);\n\n for (let i = 0; i < displayCount; i++) {\n const item = items[i];\n itemElements.push(renderLineItem(item, icon));\n }\n\n // Add \"+X more\" if there are more than 3 items (and not exactly 4)\n if (items.length > maxDisplay && items.length !== 4) {\n const remainingCount = items.length - maxDisplay;\n itemElements.push(html`<div\n style=\"display:flex;items-align:center;margin-top:0.2em;\"\n >\n ${icon\n ? html`<div style=\"margin-right:0.4em; width: 1em;\"></div>` // spacing placeholder\n : null}\n <div style=\"font-size:0.8em\">+${remainingCount} more</div>\n </div>`);\n }\n return itemElements;\n};\n\n/**\n * Renders a single flow as a clickable link that fires a temba-flow-clicked event\n */\nconst renderFlowLink = (flow: NamedObject, icon?: string) => {\n const handleClick = (e: MouseEvent) => {\n e.stopPropagation();\n e.preventDefault();\n const target = e.currentTarget as HTMLElement;\n const editor = target.closest('temba-flow-editor') as any;\n if (editor) {\n editor.fireCustomEvent(CustomEventType.FlowClicked, {\n uuid: flow.uuid,\n name: flow.name,\n metaKey: e.metaKey,\n ctrlKey: e.ctrlKey\n });\n }\n };\n\n return html`<div class=\"linked-name\" style=\"display:flex;items-align:center;\">\n ${icon\n ? html`<temba-icon name=${icon} style=\"margin-right:0.5em\"></temba-icon>`\n : null}\n <div\n @click=${handleClick}\n style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 250px; text-decoration: underline; cursor: pointer;\"\n >\n ${flow.name}\n </div>\n </div>`;\n};\n\n/**\n * Renders a list of flows as clickable links, showing up to 3 items\n * with a \"+X more\" indicator if there are more items\n */\nexport const renderFlowLinks = (flows: NamedObject[], icon?: string) => {\n const itemElements = [];\n const maxDisplay = 3;\n\n const displayCount =\n flows.length === 4 ? 4 : Math.min(maxDisplay, flows.length);\n\n for (let i = 0; i < displayCount; i++) {\n itemElements.push(renderFlowLink(flows[i], icon));\n }\n\n if (flows.length > maxDisplay && flows.length !== 4) {\n const remainingCount = flows.length - maxDisplay;\n itemElements.push(html`<div\n style=\"display:flex;items-align:center;margin-top:0.2em;\"\n >\n ${icon\n ? html`<div style=\"margin-right:0.4em; width: 1em;\"></div>`\n : null}\n <div style=\"font-size:0.8em\">+${remainingCount} more</div>\n </div>`);\n }\n return itemElements;\n};\n\nexport interface Scheme {\n scheme: string;\n name: string;\n path: string;\n excludeFromSplit?: boolean;\n}\n\nexport const SCHEMES: Scheme[] = [\n {\n scheme: 'tel',\n name: 'SMS',\n path: 'Phone Number'\n },\n {\n scheme: 'whatsapp',\n name: 'WhatsApp',\n path: 'WhatsApp Number'\n },\n {\n scheme: 'facebook',\n name: 'Facebook',\n path: 'Facebook ID'\n },\n {\n scheme: 'instagram',\n name: 'Instagram',\n path: 'Instagram ID'\n },\n {\n scheme: 'twitterid',\n name: 'Twitter',\n path: 'Twitter ID',\n excludeFromSplit: true\n },\n {\n scheme: 'telegram',\n name: 'Telegram',\n path: 'Telegram ID'\n },\n {\n scheme: 'viber',\n name: 'Viber',\n path: 'Viber ID'\n },\n {\n scheme: 'line',\n name: 'Line',\n path: 'Line ID'\n },\n {\n scheme: 'wechat',\n name: 'WeChat',\n path: 'WeChat ID'\n },\n {\n scheme: 'fcm',\n name: 'Firebase',\n path: 'Firebase ID'\n },\n {\n scheme: 'jiochat',\n name: 'JioChat',\n path: 'JioChat ID'\n },\n {\n scheme: 'freshchat',\n name: 'Freshchat',\n path: 'Freshchat ID'\n },\n {\n scheme: 'mailto',\n name: 'Email',\n path: 'Email Address',\n excludeFromSplit: true\n },\n {\n scheme: 'twitter',\n name: 'Twitter',\n path: 'Twitter Handle',\n excludeFromSplit: true\n },\n {\n scheme: 'vk',\n name: 'VK',\n path: 'VK ID'\n },\n {\n scheme: 'discord',\n name: 'Discord',\n path: 'Discord ID'\n },\n {\n scheme: 'webchat',\n name: 'Webchat',\n path: 'Webchat ID',\n excludeFromSplit: true\n },\n {\n scheme: 'rocketchat',\n name: 'RocketChat',\n path: 'RocketChat ID'\n },\n {\n scheme: 'ext',\n name: 'External',\n path: 'External ID'\n }\n];\n\n/**\n * Represents the bounding box of a node on the canvas\n */\nexport interface NodeBounds {\n uuid: string;\n left: number;\n top: number;\n right: number;\n bottom: number;\n width: number;\n height: number;\n}\n\n/**\n * Minimum vertical spacing between nodes (in pixels)\n */\nconst MIN_NODE_SPACING = 30;\n\n/**\n * Small buffer to avoid floating point precision issues in overlap detection (in pixels)\n * This prevents false positives when nodes are exactly adjacent (e.g., bottom of one node\n * at exactly the same position as top of another)\n */\nconst OVERLAP_BUFFER = 10;\n\n/**\n * Gets the bounding box for a node from the DOM\n *\n * @param nodeUuid - The UUID of the node\n * @param position - The current position of the node\n * @param element - Optional pre-fetched DOM element (recommended for performance when checking multiple nodes)\n * @returns NodeBounds object or null if element not found\n *\n * Note: When element is not provided, performs a DOM query which may impact performance\n * during bulk collision detection. Consider fetching elements beforehand when possible.\n */\nexport const getNodeBounds = (\n nodeUuid: string,\n position: FlowPosition,\n element?: HTMLElement\n): NodeBounds | null => {\n // If element is provided, use it; otherwise try to find it in DOM\n const nodeElement =\n element || (document.querySelector(`[id=\"${nodeUuid}\"]`) as HTMLElement);\n\n if (!nodeElement) {\n return null;\n }\n\n // Use offsetWidth/offsetHeight instead of getBoundingClientRect() so\n // dimensions are in CSS-layout space and unaffected by ancestor transforms (zoom).\n const width = nodeElement.offsetWidth;\n const height = nodeElement.offsetHeight;\n\n return {\n uuid: nodeUuid,\n left: position.left,\n top: position.top,\n right: position.left + width,\n bottom: position.top + height,\n width,\n height\n };\n};\n\n/**\n * Checks if two node bounding boxes overlap\n */\nexport const nodesOverlap = (\n bounds1: NodeBounds,\n bounds2: NodeBounds\n): boolean => {\n // Use a small buffer to avoid floating point precision issues\n const buffer = OVERLAP_BUFFER;\n\n return !(\n bounds1.right <= bounds2.left - buffer ||\n bounds1.left >= bounds2.right + buffer ||\n bounds1.bottom <= bounds2.top - buffer ||\n bounds1.top >= bounds2.bottom + buffer\n );\n};\n\n/**\n * Detects all collisions between a node and other nodes\n */\nexport const detectCollisions = (\n targetBounds: NodeBounds,\n allBounds: NodeBounds[]\n): NodeBounds[] => {\n return allBounds.filter(\n (bounds) =>\n bounds.uuid !== targetBounds.uuid && nodesOverlap(targetBounds, bounds)\n );\n};\n\ntype Direction = 'down' | 'up' | 'right' | 'left';\n\nconst DIRECTIONS: Direction[] = ['down', 'up', 'right', 'left'];\n\n/**\n * Creates a new NodeBounds at a different position\n */\nconst makeBoundsAt = (\n original: NodeBounds,\n left: number,\n top: number\n): NodeBounds => ({\n ...original,\n left,\n top,\n right: left + original.width,\n bottom: top + original.height\n});\n\n/**\n * Computes the minimum position needed to clear all fixed nodes in a given direction.\n * Returns null if the direction is not viable (e.g., would require negative coordinates\n * and still overlap).\n */\nconst computeDirectionalClearance = (\n collider: NodeBounds,\n fixedNodes: NodeBounds[],\n direction: Direction\n): { left: number; top: number } | null => {\n switch (direction) {\n case 'down': {\n const maxBottom = Math.max(...fixedNodes.map((f) => f.bottom));\n const newTop = snapToGrid(maxBottom + MIN_NODE_SPACING);\n return { left: collider.left, top: newTop };\n }\n case 'up': {\n const minTop = Math.min(...fixedNodes.map((f) => f.top));\n const newTop = snapToGrid(minTop - collider.height - MIN_NODE_SPACING);\n if (newTop < 0) return { left: collider.left, top: 0 };\n return { left: collider.left, top: newTop };\n }\n case 'right': {\n const maxRight = Math.max(...fixedNodes.map((f) => f.right));\n const newLeft = snapToGrid(maxRight + MIN_NODE_SPACING);\n return { left: newLeft, top: collider.top };\n }\n case 'left': {\n const minLeft = Math.min(...fixedNodes.map((f) => f.left));\n const newLeft = snapToGrid(minLeft - collider.width - MIN_NODE_SPACING);\n if (newLeft < 0) return { left: 0, top: collider.top };\n return { left: newLeft, top: collider.top };\n }\n }\n};\n\n/**\n * Calculates new positions to resolve all collisions using multi-directional reflow.\n *\n * Sacred nodes (the ones just dropped/created) keep their positions. All other\n * colliding nodes are moved in whichever direction requires the least displacement\n * and causes the fewest cascading collisions.\n */\nexport const calculateReflowPositions = (\n sacredNodeUuids: string[],\n allBounds: NodeBounds[]\n): Map<string, FlowPosition> => {\n const newPositions = new Map<string, FlowPosition>();\n const sacredSet = new Set(sacredNodeUuids);\n\n // Mutable map of current bounds, updated as collisions are resolved\n const currentBounds = new Map<string, NodeBounds>();\n for (const b of allBounds) {\n currentBounds.set(b.uuid, { ...b });\n }\n\n // A sacred node yields to an existing node at the top of the canvas when\n // the sacred wasn't dropped above it. The existing node keeps its top\n // position and the sacred node moves below instead.\n for (const sacredUuid of [...sacredSet]) {\n const sacred = currentBounds.get(sacredUuid);\n if (!sacred) continue;\n\n for (const [uuid, bounds] of currentBounds) {\n if (uuid === sacredUuid || sacredSet.has(uuid)) continue;\n if (!nodesOverlap(sacred, bounds)) continue;\n\n if (sacred.top > bounds.top && bounds.top < MIN_NODE_SPACING) {\n sacredSet.delete(sacredUuid);\n sacredSet.add(uuid);\n break;\n }\n }\n }\n\n // Seed the queue with non-sacred nodes that overlap any sacred node\n const queue: string[] = [];\n const inQueue = new Set<string>();\n\n for (const sacredUuid of sacredSet) {\n const sacred = currentBounds.get(sacredUuid);\n if (!sacred) continue;\n for (const [uuid, bounds] of currentBounds) {\n if (sacredSet.has(uuid) || inQueue.has(uuid)) continue;\n if (nodesOverlap(sacred, bounds)) {\n queue.push(uuid);\n inQueue.add(uuid);\n }\n }\n }\n\n const resolved = new Set<string>();\n let iterations = 0;\n const maxIterations = 200;\n\n while (queue.length > 0 && iterations < maxIterations) {\n iterations++;\n const uuid = queue.shift()!;\n\n if (resolved.has(uuid)) continue;\n\n const collider = currentBounds.get(uuid)!;\n\n // Find all fixed nodes (sacred + already-resolved) that overlap this node\n const fixedOverlaps: NodeBounds[] = [];\n for (const [otherUuid, otherBounds] of currentBounds) {\n if (otherUuid === uuid) continue;\n if (sacredSet.has(otherUuid) || resolved.has(otherUuid)) {\n if (nodesOverlap(collider, otherBounds)) {\n fixedOverlaps.push(otherBounds);\n }\n }\n }\n\n if (fixedOverlaps.length === 0) continue;\n\n // Determine direction constraints and axis bias from sacred node overlaps\n const sacredOverlaps = fixedOverlaps.filter((f) => sacredSet.has(f.uuid));\n const allowedDirections: Direction[] = [...DIRECTIONS];\n let axisBias: 'vertical' | 'horizontal' | null = null;\n\n if (sacredOverlaps.length > 0) {\n // Rule 1: don't move a lower node above the sacred node\n // Rule 2: don't move a right-of node to the left of the sacred node\n for (const sacred of sacredOverlaps) {\n if (collider.top > sacred.top) {\n const idx = allowedDirections.indexOf('up');\n if (idx !== -1) allowedDirections.splice(idx, 1);\n }\n if (collider.left > sacred.left) {\n const idx = allowedDirections.indexOf('left');\n if (idx !== -1) allowedDirections.splice(idx, 1);\n }\n }\n\n // Rule 3: bias direction based on overlap shape\n let totalOverlapWidth = 0;\n let totalOverlapHeight = 0;\n for (const sacred of sacredOverlaps) {\n totalOverlapWidth +=\n Math.min(collider.right, sacred.right) -\n Math.max(collider.left, sacred.left);\n totalOverlapHeight +=\n Math.min(collider.bottom, sacred.bottom) -\n Math.max(collider.top, sacred.top);\n }\n if (totalOverlapWidth > totalOverlapHeight) {\n axisBias = 'vertical'; // wide overlap = nodes stacked = prefer up/down\n } else if (totalOverlapHeight > totalOverlapWidth) {\n axisBias = 'horizontal'; // tall overlap = nodes side-by-side = prefer left/right\n }\n }\n\n // Try each allowed direction, pick the one with least disruption\n let bestPos: { left: number; top: number } | null = null;\n let bestScore = Infinity;\n\n for (const dir of allowedDirections) {\n const candidate = computeDirectionalClearance(\n collider,\n fixedOverlaps,\n dir\n );\n if (!candidate) continue;\n\n const candidateBounds = makeBoundsAt(\n collider,\n candidate.left,\n candidate.top\n );\n\n // Verify no overlap with any sacred or resolved node\n let stillOverlaps = false;\n let cascadeCount = 0;\n for (const [otherUuid, otherBounds] of currentBounds) {\n if (otherUuid === uuid) continue;\n if (!nodesOverlap(candidateBounds, otherBounds)) continue;\n\n if (sacredSet.has(otherUuid) || resolved.has(otherUuid)) {\n stillOverlaps = true;\n break;\n }\n cascadeCount++;\n }\n if (stillOverlaps) continue;\n\n const distance =\n Math.abs(candidate.left - collider.left) +\n Math.abs(candidate.top - collider.top);\n\n // When colliding with sacred nodes, use axis bias scoring;\n // for cascading collisions (no sacred overlap), use original scoring\n let score: number;\n if (sacredOverlaps.length > 0) {\n const isVerticalDir = dir === 'up' || dir === 'down';\n const axisMatch =\n axisBias === null ||\n (axisBias === 'vertical' && isVerticalDir) ||\n (axisBias === 'horizontal' && !isVerticalDir);\n const axisPenalty = axisMatch ? 0 : 5000;\n score = cascadeCount * 2000 + axisPenalty + distance;\n } else {\n score = cascadeCount * 10000 + distance;\n }\n\n if (score < bestScore) {\n bestScore = score;\n bestPos = candidate;\n }\n }\n\n if (bestPos) {\n newPositions.set(uuid, { left: bestPos.left, top: bestPos.top });\n const newBounds = makeBoundsAt(collider, bestPos.left, bestPos.top);\n currentBounds.set(uuid, newBounds);\n resolved.add(uuid);\n\n // Enqueue any new cascading collisions\n for (const [otherUuid, otherBounds] of currentBounds) {\n if (otherUuid === uuid) continue;\n if (sacredSet.has(otherUuid) || resolved.has(otherUuid)) continue;\n if (inQueue.has(otherUuid)) continue;\n if (nodesOverlap(newBounds, otherBounds)) {\n queue.push(otherUuid);\n inQueue.add(otherUuid);\n }\n }\n }\n }\n\n return newPositions;\n};\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/flow/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, TemplateResult } from 'lit-html';\nimport { NamedObject, FlowPosition } from '../store/flow-definition';\nimport { FlowIssue } from '../store/AppState';\nimport { CustomEventType } from '../interfaces';\n\nconst IS_MAC =\n typeof navigator !== 'undefined' &&\n /Mac|iPod|iPhone|iPad/.test(navigator.platform);\n\n/**\n * Returns true if the mouse event is a right-click or equivalent:\n * - button !== 0 (actual right-click or middle-click on any platform)\n * - ctrl+click on macOS (emulates right-click / context menu)\n */\nexport function isRightClick(event: MouseEvent): boolean {\n if (event.button !== 0) return true;\n if (IS_MAC && event.ctrlKey) return true;\n return false;\n}\n\nexport function formatIssueMessage(issue: FlowIssue): string {\n if (issue.dependency) {\n const name = issue.dependency.name || issue.dependency.key;\n return `Cannot find a ${issue.dependency.type} for ${name}`;\n }\n return issue.description;\n}\n\nconst GRID_SIZE = 20;\n\nexport function snapToGrid(value: number): number {\n const snapped = Math.round(value / GRID_SIZE) * GRID_SIZE;\n return Math.max(snapped, 0);\n}\n\n/**\n * Renders content clamped to a maximum number of lines with ellipsis.\n * Hovering shows the full text in a tooltip.\n */\nexport const renderClamped = (\n content: TemplateResult | string,\n titleText: string,\n maxLines: number = 3\n) => {\n return html`<div\n style=\"display: -webkit-box; -webkit-line-clamp: ${maxLines}; -webkit-box-orient: vertical; overflow: hidden; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto;\"\n title=\"${titleText}\"\n >\n ${content}\n </div>`;\n};\n\n/**\n * Renders a single line item with optional icon\n */\nexport const renderLineItem = (name: string, icon?: string) => {\n return html`<div style=\"display:flex;items-align:center;\">\n ${icon\n ? html`<temba-icon name=${icon} style=\"margin-right:0.5em\"></temba-icon>`\n : null}\n <div\n style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 250px;\"\n title=\"${name}\"\n >\n ${name}\n </div>\n </div>`;\n};\n\n/**\n * Renders a list of named objects with optional icon, showing up to 3 items\n * with a \"+X more\" indicator if there are more items\n */\nexport const renderNamedObjects = (assets: NamedObject[], icon?: string) => {\n return renderStringList(\n assets.map((asset) => asset.name),\n icon\n );\n};\n\n/**\n * Renders a list of strings with optional icon, showing up to 3 items\n * with a \"+X more\" indicator if there are more items\n */\nexport const renderStringList = (items: string[], icon?: string) => {\n const itemElements = [];\n const maxDisplay = 3;\n\n // Show up to 3 items, or all 4 if exactly 4 items\n const displayCount =\n items.length === 4 ? 4 : Math.min(maxDisplay, items.length);\n\n for (let i = 0; i < displayCount; i++) {\n const item = items[i];\n itemElements.push(renderLineItem(item, icon));\n }\n\n // Add \"+X more\" if there are more than 3 items (and not exactly 4)\n if (items.length > maxDisplay && items.length !== 4) {\n const remainingCount = items.length - maxDisplay;\n itemElements.push(html`<div\n style=\"display:flex;items-align:center;margin-top:0.2em;\"\n >\n ${icon\n ? html`<div style=\"margin-right:0.4em; width: 1em;\"></div>` // spacing placeholder\n : null}\n <div style=\"font-size:0.8em\">+${remainingCount} more</div>\n </div>`);\n }\n return itemElements;\n};\n\n/**\n * Renders a single flow as a clickable link that fires a temba-flow-clicked event\n */\nconst renderFlowLink = (flow: NamedObject, icon?: string) => {\n const handleClick = (e: MouseEvent) => {\n e.stopPropagation();\n e.preventDefault();\n const target = e.currentTarget as HTMLElement;\n const editor = target.closest('temba-flow-editor') as any;\n if (editor) {\n editor.fireCustomEvent(CustomEventType.FlowClicked, {\n uuid: flow.uuid,\n name: flow.name,\n metaKey: e.metaKey,\n ctrlKey: e.ctrlKey\n });\n }\n };\n\n return html`<div class=\"linked-name\" style=\"display:flex;items-align:center;\">\n ${icon\n ? html`<temba-icon name=${icon} style=\"margin-right:0.5em\"></temba-icon>`\n : null}\n <div\n @click=${handleClick}\n style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 250px; text-decoration: underline; cursor: pointer;\"\n >\n ${flow.name}\n </div>\n </div>`;\n};\n\n/**\n * Renders a list of flows as clickable links, showing up to 3 items\n * with a \"+X more\" indicator if there are more items\n */\nexport const renderFlowLinks = (flows: NamedObject[], icon?: string) => {\n const itemElements = [];\n const maxDisplay = 3;\n\n const displayCount =\n flows.length === 4 ? 4 : Math.min(maxDisplay, flows.length);\n\n for (let i = 0; i < displayCount; i++) {\n itemElements.push(renderFlowLink(flows[i], icon));\n }\n\n if (flows.length > maxDisplay && flows.length !== 4) {\n const remainingCount = flows.length - maxDisplay;\n itemElements.push(html`<div\n style=\"display:flex;items-align:center;margin-top:0.2em;\"\n >\n ${icon ? html`<div style=\"margin-right:0.4em; width: 1em;\"></div>` : null}\n <div style=\"font-size:0.8em\">+${remainingCount} more</div>\n </div>`);\n }\n return itemElements;\n};\n\nexport interface Scheme {\n scheme: string;\n name: string;\n path: string;\n excludeFromSplit?: boolean;\n}\n\nexport const SCHEMES: Scheme[] = [\n {\n scheme: 'tel',\n name: 'SMS',\n path: 'Phone Number'\n },\n {\n scheme: 'whatsapp',\n name: 'WhatsApp',\n path: 'WhatsApp Number'\n },\n {\n scheme: 'facebook',\n name: 'Facebook',\n path: 'Facebook ID'\n },\n {\n scheme: 'instagram',\n name: 'Instagram',\n path: 'Instagram ID'\n },\n {\n scheme: 'twitterid',\n name: 'Twitter',\n path: 'Twitter ID',\n excludeFromSplit: true\n },\n {\n scheme: 'telegram',\n name: 'Telegram',\n path: 'Telegram ID'\n },\n {\n scheme: 'viber',\n name: 'Viber',\n path: 'Viber ID'\n },\n {\n scheme: 'line',\n name: 'Line',\n path: 'Line ID'\n },\n {\n scheme: 'wechat',\n name: 'WeChat',\n path: 'WeChat ID'\n },\n {\n scheme: 'fcm',\n name: 'Firebase',\n path: 'Firebase ID'\n },\n {\n scheme: 'jiochat',\n name: 'JioChat',\n path: 'JioChat ID'\n },\n {\n scheme: 'freshchat',\n name: 'Freshchat',\n path: 'Freshchat ID'\n },\n {\n scheme: 'mailto',\n name: 'Email',\n path: 'Email Address',\n excludeFromSplit: true\n },\n {\n scheme: 'twitter',\n name: 'Twitter',\n path: 'Twitter Handle',\n excludeFromSplit: true\n },\n {\n scheme: 'vk',\n name: 'VK',\n path: 'VK ID'\n },\n {\n scheme: 'discord',\n name: 'Discord',\n path: 'Discord ID'\n },\n {\n scheme: 'webchat',\n name: 'Webchat',\n path: 'Webchat ID',\n excludeFromSplit: true\n },\n {\n scheme: 'rocketchat',\n name: 'RocketChat',\n path: 'RocketChat ID'\n },\n {\n scheme: 'ext',\n name: 'External',\n path: 'External ID'\n }\n];\n\n/**\n * Represents the bounding box of a node on the canvas\n */\nexport interface NodeBounds {\n uuid: string;\n left: number;\n top: number;\n right: number;\n bottom: number;\n width: number;\n height: number;\n}\n\n/**\n * Minimum vertical spacing between nodes (in pixels)\n */\nconst MIN_NODE_SPACING = 30;\n\n/**\n * Small buffer to avoid floating point precision issues in overlap detection (in pixels)\n * This prevents false positives when nodes are exactly adjacent (e.g., bottom of one node\n * at exactly the same position as top of another)\n */\nconst OVERLAP_BUFFER = 10;\n\n/**\n * Gets the bounding box for a node from the DOM\n *\n * @param nodeUuid - The UUID of the node\n * @param position - The current position of the node\n * @param element - Optional pre-fetched DOM element (recommended for performance when checking multiple nodes)\n * @returns NodeBounds object or null if element not found\n *\n * Note: When element is not provided, performs a DOM query which may impact performance\n * during bulk collision detection. Consider fetching elements beforehand when possible.\n */\nexport const getNodeBounds = (\n nodeUuid: string,\n position: FlowPosition,\n element?: HTMLElement\n): NodeBounds | null => {\n // If element is provided, use it; otherwise try to find it in DOM\n const nodeElement =\n element || (document.querySelector(`[id=\"${nodeUuid}\"]`) as HTMLElement);\n\n if (!nodeElement) {\n return null;\n }\n\n // Use offsetWidth/offsetHeight instead of getBoundingClientRect() so\n // dimensions are in CSS-layout space and unaffected by ancestor transforms (zoom).\n const width = nodeElement.offsetWidth;\n const height = nodeElement.offsetHeight;\n\n return {\n uuid: nodeUuid,\n left: position.left,\n top: position.top,\n right: position.left + width,\n bottom: position.top + height,\n width,\n height\n };\n};\n\n/**\n * Checks if two node bounding boxes overlap\n */\nexport const nodesOverlap = (\n bounds1: NodeBounds,\n bounds2: NodeBounds\n): boolean => {\n // Use a small buffer to avoid floating point precision issues\n const buffer = OVERLAP_BUFFER;\n\n return !(\n bounds1.right <= bounds2.left - buffer ||\n bounds1.left >= bounds2.right + buffer ||\n bounds1.bottom <= bounds2.top - buffer ||\n bounds1.top >= bounds2.bottom + buffer\n );\n};\n\n/**\n * Detects all collisions between a node and other nodes\n */\nexport const detectCollisions = (\n targetBounds: NodeBounds,\n allBounds: NodeBounds[]\n): NodeBounds[] => {\n return allBounds.filter(\n (bounds) =>\n bounds.uuid !== targetBounds.uuid && nodesOverlap(targetBounds, bounds)\n );\n};\n\ntype Direction = 'down' | 'up' | 'right' | 'left';\n\nconst DIRECTIONS: Direction[] = ['down', 'up', 'right', 'left'];\n\n/**\n * Creates a new NodeBounds at a different position\n */\nconst makeBoundsAt = (\n original: NodeBounds,\n left: number,\n top: number\n): NodeBounds => ({\n ...original,\n left,\n top,\n right: left + original.width,\n bottom: top + original.height\n});\n\n/**\n * Computes the minimum position needed to clear all fixed nodes in a given direction.\n * Returns null if the direction is not viable (e.g., would require negative coordinates\n * and still overlap).\n */\nconst computeDirectionalClearance = (\n collider: NodeBounds,\n fixedNodes: NodeBounds[],\n direction: Direction\n): { left: number; top: number } | null => {\n switch (direction) {\n case 'down': {\n const maxBottom = Math.max(...fixedNodes.map((f) => f.bottom));\n const newTop = snapToGrid(maxBottom + MIN_NODE_SPACING);\n return { left: collider.left, top: newTop };\n }\n case 'up': {\n const minTop = Math.min(...fixedNodes.map((f) => f.top));\n const newTop = snapToGrid(minTop - collider.height - MIN_NODE_SPACING);\n if (newTop < 0) return { left: collider.left, top: 0 };\n return { left: collider.left, top: newTop };\n }\n case 'right': {\n const maxRight = Math.max(...fixedNodes.map((f) => f.right));\n const newLeft = snapToGrid(maxRight + MIN_NODE_SPACING);\n return { left: newLeft, top: collider.top };\n }\n case 'left': {\n const minLeft = Math.min(...fixedNodes.map((f) => f.left));\n const newLeft = snapToGrid(minLeft - collider.width - MIN_NODE_SPACING);\n if (newLeft < 0) return { left: 0, top: collider.top };\n return { left: newLeft, top: collider.top };\n }\n }\n};\n\n/**\n * Calculates new positions to resolve all collisions using multi-directional reflow.\n *\n * Sacred nodes (the ones just dropped/created) keep their positions. All other\n * colliding nodes are moved in whichever direction requires the least displacement\n * and causes the fewest cascading collisions.\n */\nexport const calculateReflowPositions = (\n sacredNodeUuids: string[],\n allBounds: NodeBounds[]\n): Map<string, FlowPosition> => {\n const newPositions = new Map<string, FlowPosition>();\n const sacredSet = new Set(sacredNodeUuids);\n\n // Mutable map of current bounds, updated as collisions are resolved\n const currentBounds = new Map<string, NodeBounds>();\n for (const b of allBounds) {\n currentBounds.set(b.uuid, { ...b });\n }\n\n // A sacred node yields to an existing node at the top of the canvas when\n // the sacred wasn't dropped above it. The existing node keeps its top\n // position and the sacred node moves below instead.\n for (const sacredUuid of [...sacredSet]) {\n const sacred = currentBounds.get(sacredUuid);\n if (!sacred) continue;\n\n for (const [uuid, bounds] of currentBounds) {\n if (uuid === sacredUuid || sacredSet.has(uuid)) continue;\n if (!nodesOverlap(sacred, bounds)) continue;\n\n if (sacred.top > bounds.top && bounds.top < MIN_NODE_SPACING) {\n sacredSet.delete(sacredUuid);\n sacredSet.add(uuid);\n break;\n }\n }\n }\n\n // Seed the queue with non-sacred nodes that overlap any sacred node\n const queue: string[] = [];\n const inQueue = new Set<string>();\n\n for (const sacredUuid of sacredSet) {\n const sacred = currentBounds.get(sacredUuid);\n if (!sacred) continue;\n for (const [uuid, bounds] of currentBounds) {\n if (sacredSet.has(uuid) || inQueue.has(uuid)) continue;\n if (nodesOverlap(sacred, bounds)) {\n queue.push(uuid);\n inQueue.add(uuid);\n }\n }\n }\n\n const resolved = new Set<string>();\n let iterations = 0;\n const maxIterations = 200;\n\n while (queue.length > 0 && iterations < maxIterations) {\n iterations++;\n const uuid = queue.shift()!;\n\n if (resolved.has(uuid)) continue;\n\n const collider = currentBounds.get(uuid)!;\n\n // Find all fixed nodes (sacred + already-resolved) that overlap this node\n const fixedOverlaps: NodeBounds[] = [];\n for (const [otherUuid, otherBounds] of currentBounds) {\n if (otherUuid === uuid) continue;\n if (sacredSet.has(otherUuid) || resolved.has(otherUuid)) {\n if (nodesOverlap(collider, otherBounds)) {\n fixedOverlaps.push(otherBounds);\n }\n }\n }\n\n if (fixedOverlaps.length === 0) continue;\n\n // Determine direction constraints and axis bias from sacred node overlaps\n const sacredOverlaps = fixedOverlaps.filter((f) => sacredSet.has(f.uuid));\n const allowedDirections: Direction[] = [...DIRECTIONS];\n let axisBias: 'vertical' | 'horizontal' | null = null;\n\n if (sacredOverlaps.length > 0) {\n // Rule 1: don't move a lower node above the sacred node\n // Rule 2: don't move a right-of node to the left of the sacred node\n for (const sacred of sacredOverlaps) {\n if (collider.top > sacred.top) {\n const idx = allowedDirections.indexOf('up');\n if (idx !== -1) allowedDirections.splice(idx, 1);\n }\n if (collider.left > sacred.left) {\n const idx = allowedDirections.indexOf('left');\n if (idx !== -1) allowedDirections.splice(idx, 1);\n }\n }\n\n // Rule 3: bias direction based on overlap shape\n let totalOverlapWidth = 0;\n let totalOverlapHeight = 0;\n for (const sacred of sacredOverlaps) {\n totalOverlapWidth +=\n Math.min(collider.right, sacred.right) -\n Math.max(collider.left, sacred.left);\n totalOverlapHeight +=\n Math.min(collider.bottom, sacred.bottom) -\n Math.max(collider.top, sacred.top);\n }\n if (totalOverlapWidth > totalOverlapHeight) {\n axisBias = 'vertical'; // wide overlap = nodes stacked = prefer up/down\n } else if (totalOverlapHeight > totalOverlapWidth) {\n axisBias = 'horizontal'; // tall overlap = nodes side-by-side = prefer left/right\n }\n }\n\n // Try each allowed direction, pick the one with least disruption\n let bestPos: { left: number; top: number } | null = null;\n let bestScore = Infinity;\n\n for (const dir of allowedDirections) {\n const candidate = computeDirectionalClearance(\n collider,\n fixedOverlaps,\n dir\n );\n if (!candidate) continue;\n\n const candidateBounds = makeBoundsAt(\n collider,\n candidate.left,\n candidate.top\n );\n\n // Verify no overlap with any sacred or resolved node\n let stillOverlaps = false;\n let cascadeCount = 0;\n for (const [otherUuid, otherBounds] of currentBounds) {\n if (otherUuid === uuid) continue;\n if (!nodesOverlap(candidateBounds, otherBounds)) continue;\n\n if (sacredSet.has(otherUuid) || resolved.has(otherUuid)) {\n stillOverlaps = true;\n break;\n }\n cascadeCount++;\n }\n if (stillOverlaps) continue;\n\n const distance =\n Math.abs(candidate.left - collider.left) +\n Math.abs(candidate.top - collider.top);\n\n // When colliding with sacred nodes, use axis bias scoring;\n // for cascading collisions (no sacred overlap), use original scoring\n let score: number;\n if (sacredOverlaps.length > 0) {\n const isVerticalDir = dir === 'up' || dir === 'down';\n const axisMatch =\n axisBias === null ||\n (axisBias === 'vertical' && isVerticalDir) ||\n (axisBias === 'horizontal' && !isVerticalDir);\n const axisPenalty = axisMatch ? 0 : 5000;\n score = cascadeCount * 2000 + axisPenalty + distance;\n } else {\n score = cascadeCount * 10000 + distance;\n }\n\n if (score < bestScore) {\n bestScore = score;\n bestPos = candidate;\n }\n }\n\n if (bestPos) {\n newPositions.set(uuid, { left: bestPos.left, top: bestPos.top });\n const newBounds = makeBoundsAt(collider, bestPos.left, bestPos.top);\n currentBounds.set(uuid, newBounds);\n resolved.add(uuid);\n\n // Enqueue any new cascading collisions\n for (const [otherUuid, otherBounds] of currentBounds) {\n if (otherUuid === uuid) continue;\n if (sacredSet.has(otherUuid) || resolved.has(otherUuid)) continue;\n if (inQueue.has(otherUuid)) continue;\n if (nodesOverlap(newBounds, otherBounds)) {\n queue.push(otherUuid);\n inQueue.add(otherUuid);\n }\n }\n }\n }\n\n return newPositions;\n};\n"]}