@nyaruka/temba-components 0.126.0 → 0.128.0

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 (72) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/demo/chart/example.html +18 -1
  3. package/demo/data/flows/sample-flow.json +127 -100
  4. package/demo/data/server/opened-tickets-long.json +53 -0
  5. package/demo/sticky-note-demo.html +152 -0
  6. package/dist/locales/es.js +5 -5
  7. package/dist/locales/es.js.map +1 -1
  8. package/dist/locales/fr.js +5 -5
  9. package/dist/locales/fr.js.map +1 -1
  10. package/dist/locales/locale-codes.js +11 -2
  11. package/dist/locales/locale-codes.js.map +1 -1
  12. package/dist/locales/pt.js +5 -5
  13. package/dist/locales/pt.js.map +1 -1
  14. package/dist/temba-components.js +346 -86
  15. package/dist/temba-components.js.map +1 -1
  16. package/out-tsc/src/chart/TembaChart.js +44 -5
  17. package/out-tsc/src/chart/TembaChart.js.map +1 -1
  18. package/out-tsc/src/flow/Editor.js +210 -1
  19. package/out-tsc/src/flow/Editor.js.map +1 -1
  20. package/out-tsc/src/flow/EditorNode.js +98 -142
  21. package/out-tsc/src/flow/EditorNode.js.map +1 -1
  22. package/out-tsc/src/flow/StickyNote.js +272 -0
  23. package/out-tsc/src/flow/StickyNote.js.map +1 -0
  24. package/out-tsc/src/list/RunList.js +2 -1
  25. package/out-tsc/src/list/RunList.js.map +1 -1
  26. package/out-tsc/src/list/SortableList.js +9 -0
  27. package/out-tsc/src/list/SortableList.js.map +1 -1
  28. package/out-tsc/src/locales/es.js +5 -5
  29. package/out-tsc/src/locales/es.js.map +1 -1
  30. package/out-tsc/src/locales/fr.js +5 -5
  31. package/out-tsc/src/locales/fr.js.map +1 -1
  32. package/out-tsc/src/locales/locale-codes.js +11 -2
  33. package/out-tsc/src/locales/locale-codes.js.map +1 -1
  34. package/out-tsc/src/locales/pt.js +5 -5
  35. package/out-tsc/src/locales/pt.js.map +1 -1
  36. package/out-tsc/src/store/AppState.js +33 -0
  37. package/out-tsc/src/store/AppState.js.map +1 -1
  38. package/out-tsc/src/vectoricon/index.js +2 -1
  39. package/out-tsc/src/vectoricon/index.js.map +1 -1
  40. package/out-tsc/temba-modules.js +2 -0
  41. package/out-tsc/temba-modules.js.map +1 -1
  42. package/out-tsc/test/temba-flow-editor-node.test.js +249 -5
  43. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
  44. package/out-tsc/test/temba-select.test.js +9 -14
  45. package/out-tsc/test/temba-select.test.js.map +1 -1
  46. package/out-tsc/test/utils.test.js +62 -0
  47. package/out-tsc/test/utils.test.js.map +1 -1
  48. package/package.json +1 -1
  49. package/screenshots/truth/sticky-note/blue.png +0 -0
  50. package/screenshots/truth/sticky-note/gray.png +0 -0
  51. package/screenshots/truth/sticky-note/green.png +0 -0
  52. package/screenshots/truth/sticky-note/pink.png +0 -0
  53. package/screenshots/truth/sticky-note/yellow.png +0 -0
  54. package/src/chart/TembaChart.ts +47 -5
  55. package/src/flow/Editor.ts +252 -2
  56. package/src/flow/EditorNode.ts +98 -160
  57. package/src/flow/StickyNote.ts +284 -0
  58. package/src/list/RunList.ts +2 -1
  59. package/src/list/SortableList.ts +11 -0
  60. package/src/locales/es.ts +18 -13
  61. package/src/locales/fr.ts +18 -13
  62. package/src/locales/locale-codes.ts +11 -2
  63. package/src/locales/pt.ts +18 -13
  64. package/src/store/AppState.ts +51 -1
  65. package/src/store/flow-definition.d.ts +8 -0
  66. package/src/vectoricon/index.ts +2 -1
  67. package/static/svg/index.pdf +137 -0
  68. package/temba-modules.ts +2 -0
  69. package/test/temba-flow-editor-node.test.ts +322 -6
  70. package/test/temba-select.test.ts +10 -17
  71. package/test/utils.test.ts +98 -0
  72. package/web-dev-server.config.mjs +30 -22
@@ -1 +1 @@
1
- {"version":3,"file":"Editor.js","sourceRoot":"","sources":["../../../src/flow/Editor.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, TemplateResult } from 'lit-html';\nimport { css, PropertyValueMap, unsafeCSS } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { FlowDefinition } from '../store/flow-definition';\nimport { getStore } from '../store/Store';\nimport { AppState, fromStore, zustand } from '../store/AppState';\nimport { RapidElement } from '../RapidElement';\n\nimport { Plumber } from './Plumber';\nimport { EditorNode } from './EditorNode';\n\nconst SAVE_QUIET_TIME = 500;\n\nexport class Editor extends RapidElement {\n // unfortunately, jsplumb requires that we be in light DOM\n createRenderRoot() {\n return this;\n }\n\n // this is the master plumber\n private plumber: Plumber;\n\n // timer for debounced saving\n private saveTimer: number | null = null;\n\n @property({ type: String })\n public flow: string;\n\n @property({ type: String })\n public version: string;\n\n @fromStore(zustand, (state: AppState) => state.flowDefinition)\n private definition!: FlowDefinition;\n\n @fromStore(zustand, (state: AppState) => state.canvasSize)\n private canvasSize!: { width: number; height: number };\n\n @fromStore(zustand, (state: AppState) => state.dirtyDate)\n private dirtyDate!: Date;\n\n static get styles() {\n return css`\n #editor {\n overflow: scroll;\n flex: 1;\n }\n\n #grid {\n position: relative;\n background-color: #f9f9f9;\n background-position: 10px 10px;\n background-image: linear-gradient(\n 0deg,\n transparent 24%,\n rgba(61, 177, 255, 0.15) 25%,\n rgba(61, 177, 255, 0.15) 26%,\n transparent 27%,\n transparent 74%,\n rgba(61, 177, 255, 0.15) 75%,\n rgba(61, 177, 255, 0.15) 76%,\n transparent 77%,\n transparent\n ),\n linear-gradient(\n 90deg,\n transparent 24%,\n rgba(61, 177, 255, 0.15) 25%,\n rgba(61, 177, 255, 0.15) 26%,\n transparent 27%,\n transparent 74%,\n rgba(61, 177, 255, 0.15) 75%,\n rgba(61, 177, 255, 0.15) 76%,\n transparent 77%,\n transparent\n );\n background-size: 40px 40px;\n box-shadow: inset -5px 0 10px rgba(0, 0, 0, 0.05);\n border-top: 1px solid #e0e0e0;\n display: inline-block;\n width: 100%;\n }\n\n #canvas {\n position: relative;\n padding: 20px;\n margin: 20px;\n }\n\n body .jtk-endpoint {\n width: initial;\n height: initial;\n }\n\n .jtk-endpoint {\n z-index: 600;\n }\n\n .plumb-source {\n z-index: 600;\n border: 0px solid var(--color-connectors);\n }\n\n .plumb-source.connected {\n box-shadow: 0 3px 3px 0px rgba(0, 0, 0, 0.1);\n border-radius: 50%;\n }\n\n .plumb-source circle {\n fill: tomato;\n }\n\n .plumb-source.connected circle {\n fill: #fff;\n }\n\n .plumb-source svg {\n fill: var(--color-connectors) !important;\n stroke: var(--color-connectors);\n }\n\n .plumb-target {\n margin-top: -6px;\n z-index: 600;\n opacity: 0;\n cursor: pointer;\n }\n\n body .plumb-connector path {\n stroke: var(--color-connectors) !important;\n stroke-width: 3px;\n z-index: 10;\n }\n\n body .plumb-connector {\n z-index: 10;\n }\n\n body .plumb-connector.elevated {\n z-index: 550;\n }\n\n body .plumb-connector.elevated path {\n stroke: var(--color-connectors) !important;\n stroke-width: 3px;\n z-index: 550;\n }\n\n body .plumb-connector.elevated .plumb-arrow {\n fill: var(--color-connectors);\n stroke: var(--color-connectors);\n stroke-width: 0px;\n margin-top: 6px;\n z-index: 550;\n }\n\n body .plumb-connector .plumb-arrow {\n fill: var(--color-connectors);\n stroke: var(--color-connectors);\n stroke-width: 0px;\n margin-top: 6px;\n z-index: 10;\n }\n\n body svg.jtk-connector.jtk-hover path {\n stroke: var(--color-success) !important;\n stroke-width: 3px;\n }\n\n body .plumb-connector.jtk-hover .plumb-arrow {\n fill: var(--color-success) !important;\n stroke-width: 0px;\n z-index: 10;\n }\n `;\n }\n\n constructor() {\n super();\n }\n\n protected firstUpdated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.firstUpdated(changes);\n this.plumber = new Plumber(this.querySelector('#canvas'));\n if (changes.has('flow')) {\n getStore().getState().fetchRevision(`/flow/revisions/${this.flow}`);\n }\n }\n\n protected updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n if (changes.has('canvasSize')) {\n // console.log('Setting canvas size', this.canvasSize);\n }\n\n if (changes.has('dirtyDate')) {\n if (this.dirtyDate) {\n this.debouncedSave();\n }\n }\n }\n\n private debouncedSave(): void {\n // Clear any existing timer\n if (this.saveTimer !== null) {\n clearTimeout(this.saveTimer);\n }\n\n this.saveTimer = window.setTimeout(() => {\n const now = new Date();\n const timeSinceLastChange = now.getTime() - this.dirtyDate.getTime();\n\n if (timeSinceLastChange >= SAVE_QUIET_TIME) {\n this.saveChanges();\n this.saveTimer = null;\n } else {\n this.debouncedSave();\n }\n }, SAVE_QUIET_TIME);\n }\n\n private saveChanges(): void {\n // post the flow definition to the server\n getStore().postJSON(`/flow/revisions/${this.flow}`, this.definition);\n getStore().getState().setDirtyDate(null);\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n if (this.saveTimer !== null) {\n clearTimeout(this.saveTimer);\n this.saveTimer = null;\n }\n }\n\n public render(): TemplateResult {\n // we have to embed our own style since we are in light DOM\n const style = html`<style>\n ${unsafeCSS(Editor.styles.cssText)}\n ${unsafeCSS(EditorNode.styles.cssText)}\n </style>`;\n\n return html`${style}\n <div id=\"editor\">\n <div\n id=\"grid\"\n style=\"min-width:100%;width:${this.canvasSize.width}px; height:${this\n .canvasSize.height}px\"\n >\n <div id=\"canvas\">\n ${this.definition\n ? this.definition.nodes.map((node) => {\n return html`<temba-flow-node\n .plumber=${this.plumber}\n .node=${node}\n .ui=${this.definition._ui.nodes[node.uuid]}\n ></temba-flow-node>`;\n })\n : html`<temba-loading></temba-loading>`}\n </div>\n </div>\n </div>`;\n }\n}\n"]}
1
+ {"version":3,"file":"Editor.js","sourceRoot":"","sources":["../../../src/flow/Editor.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, TemplateResult } from 'lit-html';\nimport { css, PropertyValueMap, unsafeCSS } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { FlowDefinition, FlowPosition } from '../store/flow-definition';\nimport { getStore } from '../store/Store';\nimport { AppState, fromStore, zustand } from '../store/AppState';\nimport { RapidElement } from '../RapidElement';\n\nimport { Plumber } from './Plumber';\nimport { EditorNode } from './EditorNode';\n\nexport function snapToGrid(value: number): number {\n return Math.round(value / 20) * 20;\n}\n\nconst SAVE_QUIET_TIME = 500;\n\nexport interface DraggableItem {\n uuid: string;\n position: FlowPosition;\n element: HTMLElement;\n type: 'node' | 'sticky';\n}\n\nconst DRAG_THRESHOLD = 10;\n\nexport class Editor extends RapidElement {\n // unfortunately, jsplumb requires that we be in light DOM\n createRenderRoot() {\n return this;\n }\n\n // this is the master plumber\n private plumber: Plumber;\n\n // timer for debounced saving\n private saveTimer: number | null = null;\n\n @property({ type: String })\n public flow: string;\n\n @property({ type: String })\n public version: string;\n\n @fromStore(zustand, (state: AppState) => state.flowDefinition)\n private definition!: FlowDefinition;\n\n @fromStore(zustand, (state: AppState) => state.canvasSize)\n private canvasSize!: { width: number; height: number };\n\n @fromStore(zustand, (state: AppState) => state.dirtyDate)\n private dirtyDate!: Date;\n\n // Drag state\n @state()\n private isDragging = false;\n private isMouseDown = false;\n private dragStartPos = { x: 0, y: 0 };\n\n @state()\n private currentDragItem: DraggableItem | null = null;\n private startPos = { left: 0, top: 0 };\n\n // Bound event handlers to maintain proper 'this' context\n private boundMouseMove = this.handleMouseMove.bind(this);\n private boundMouseUp = this.handleMouseUp.bind(this);\n\n static get styles() {\n return css`\n #editor {\n overflow: scroll;\n flex: 1;\n }\n\n #grid {\n position: relative;\n background-color: #f9f9f9;\n background-position: 10px 10px;\n background-image: linear-gradient(\n 0deg,\n transparent 24%,\n rgba(61, 177, 255, 0.15) 25%,\n rgba(61, 177, 255, 0.15) 26%,\n transparent 27%,\n transparent 74%,\n rgba(61, 177, 255, 0.15) 75%,\n rgba(61, 177, 255, 0.15) 76%,\n transparent 77%,\n transparent\n ),\n linear-gradient(\n 90deg,\n transparent 24%,\n rgba(61, 177, 255, 0.15) 25%,\n rgba(61, 177, 255, 0.15) 26%,\n transparent 27%,\n transparent 74%,\n rgba(61, 177, 255, 0.15) 75%,\n rgba(61, 177, 255, 0.15) 76%,\n transparent 77%,\n transparent\n );\n background-size: 40px 40px;\n box-shadow: inset -5px 0 10px rgba(0, 0, 0, 0.05);\n border-top: 1px solid #e0e0e0;\n display: inline-block;\n width: 100%;\n }\n\n #canvas {\n position: relative;\n padding: 20px;\n margin: 20px;\n }\n\n #canvas > .draggable {\n position: absolute;\n z-index: 100;\n }\n\n #canvas > .dragging {\n z-index: 10000 !important;\n }\n\n body .jtk-endpoint {\n width: initial;\n height: initial;\n }\n\n .jtk-endpoint {\n z-index: 600;\n }\n\n .plumb-source {\n z-index: 600;\n border: 0px solid var(--color-connectors);\n }\n\n .plumb-source.connected {\n box-shadow: 0 3px 3px 0px rgba(0, 0, 0, 0.1);\n border-radius: 50%;\n }\n\n .plumb-source circle {\n fill: tomato;\n }\n\n .plumb-source.connected circle {\n fill: #fff;\n }\n\n .plumb-source svg {\n fill: var(--color-connectors) !important;\n stroke: var(--color-connectors);\n }\n\n .plumb-target {\n margin-top: -6px;\n z-index: 600;\n opacity: 0;\n cursor: pointer;\n }\n\n body .plumb-connector path {\n stroke: var(--color-connectors) !important;\n stroke-width: 3px;\n z-index: 10;\n }\n\n body .plumb-connector {\n z-index: 10;\n }\n\n body .plumb-connector.elevated {\n z-index: 550;\n }\n\n body .plumb-connector.elevated path {\n stroke: var(--color-connectors) !important;\n stroke-width: 3px;\n z-index: 550;\n }\n\n body .plumb-connector.elevated .plumb-arrow {\n fill: var(--color-connectors);\n stroke: var(--color-connectors);\n stroke-width: 0px;\n margin-top: 6px;\n z-index: 550;\n }\n\n body .plumb-connector .plumb-arrow {\n fill: var(--color-connectors);\n stroke: var(--color-connectors);\n stroke-width: 0px;\n margin-top: 6px;\n z-index: 10;\n }\n\n body svg.jtk-connector.jtk-hover path {\n stroke: var(--color-success) !important;\n stroke-width: 3px;\n }\n\n body .plumb-connector.jtk-hover .plumb-arrow {\n fill: var(--color-success) !important;\n stroke-width: 0px;\n z-index: 10;\n }\n `;\n }\n\n constructor() {\n super();\n }\n\n protected firstUpdated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.firstUpdated(changes);\n this.plumber = new Plumber(this.querySelector('#canvas'));\n this.setupGlobalEventListeners();\n if (changes.has('flow')) {\n getStore().getState().fetchRevision(`/flow/revisions/${this.flow}`);\n }\n }\n\n protected updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n if (changes.has('canvasSize')) {\n // console.log('Setting canvas size', this.canvasSize);\n }\n\n if (changes.has('definition')) {\n this.updateCanvasSize();\n }\n\n if (changes.has('dirtyDate')) {\n if (this.dirtyDate) {\n this.debouncedSave();\n }\n }\n }\n\n private debouncedSave(): void {\n // Clear any existing timer\n if (this.saveTimer !== null) {\n clearTimeout(this.saveTimer);\n }\n\n this.saveTimer = window.setTimeout(() => {\n const now = new Date();\n const timeSinceLastChange = now.getTime() - this.dirtyDate.getTime();\n\n if (timeSinceLastChange >= SAVE_QUIET_TIME) {\n this.saveChanges();\n this.saveTimer = null;\n } else {\n this.debouncedSave();\n }\n }, SAVE_QUIET_TIME);\n }\n\n private saveChanges(): void {\n // post the flow definition to the server\n getStore().postJSON(`/flow/revisions/${this.flow}`, this.definition);\n getStore().getState().setDirtyDate(null);\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n if (this.saveTimer !== null) {\n clearTimeout(this.saveTimer);\n this.saveTimer = null;\n }\n document.removeEventListener('mousemove', this.boundMouseMove);\n document.removeEventListener('mouseup', this.boundMouseUp);\n }\n\n private setupGlobalEventListeners(): void {\n document.addEventListener('mousemove', this.boundMouseMove);\n document.addEventListener('mouseup', this.boundMouseUp);\n }\n\n private getPosition(uuid: string, type: 'node' | 'sticky'): FlowPosition {\n if (type === 'node') {\n return this.definition._ui.nodes[uuid]?.position;\n } else {\n return this.definition._ui.stickies?.[uuid]?.position;\n }\n }\n\n private updatePosition(\n uuid: string,\n type: 'node' | 'sticky',\n position: FlowPosition\n ): void {\n if (type === 'node') {\n getStore().getState().updateNodePosition(uuid, position);\n } else {\n const currentSticky = this.definition._ui.stickies?.[uuid];\n if (currentSticky) {\n getStore()\n .getState()\n .updateStickyNote(uuid, {\n ...currentSticky,\n position\n });\n }\n }\n }\n\n private handleMouseDown(event: MouseEvent): void {\n const element = event.currentTarget as HTMLElement;\n // Only start dragging if clicking on the element itself, not on exits or other interactive elements\n const target = event.target as HTMLElement;\n if (target.classList.contains('exit') || target.closest('.exit')) {\n return;\n }\n\n const uuid = element.getAttribute('uuid');\n const type = element.tagName === 'TEMBA-FLOW-NODE' ? 'node' : 'sticky';\n\n const position = this.getPosition(uuid, type);\n if (!position) return;\n\n // Set up potential drag state, but don't start dragging yet\n this.isMouseDown = true;\n this.dragStartPos = { x: event.clientX, y: event.clientY };\n this.startPos = { left: position.left, top: position.top };\n this.currentDragItem = {\n uuid,\n position,\n element,\n type\n };\n\n event.preventDefault();\n event.stopPropagation();\n }\n\n private handleMouseMove(event: MouseEvent): void {\n if (!this.isMouseDown || !this.currentDragItem) return;\n\n const deltaX = event.clientX - this.dragStartPos.x;\n const deltaY = event.clientY - this.dragStartPos.y;\n const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\n // Only start dragging if we've moved beyond the threshold\n if (!this.isDragging && distance > DRAG_THRESHOLD) {\n this.isDragging = true;\n\n // If this is a node, elevate connections\n if (this.currentDragItem.type === 'node' && this.plumber) {\n this.plumber.elevateNodeConnections(this.currentDragItem.uuid);\n }\n }\n\n // If we're actually dragging, update positions\n if (this.isDragging) {\n const newLeft = this.startPos.left + deltaX;\n const newTop = this.startPos.top + deltaY;\n\n // Update the visual position during drag\n this.currentDragItem.element.style.left = `${newLeft}px`;\n this.currentDragItem.element.style.top = `${newTop}px`;\n\n // Repaint connections if this is a node\n if (this.currentDragItem.type === 'node' && this.plumber) {\n this.plumber.repaintEverything();\n }\n }\n }\n\n private handleMouseUp(event: MouseEvent): void {\n if (!this.isMouseDown || !this.currentDragItem) return;\n\n // If we were actually dragging, handle the drag end\n if (this.isDragging) {\n // Restore normal z-index for node connections\n if (this.currentDragItem.type === 'node' && this.plumber) {\n this.plumber.restoreNodeConnections(this.currentDragItem.uuid);\n }\n\n const deltaX = event.clientX - this.dragStartPos.x;\n const deltaY = event.clientY - this.dragStartPos.y;\n\n const newLeft = this.startPos.left + deltaX;\n const newTop = this.startPos.top + deltaY;\n\n // Snap to 20px grid for final position\n const snappedLeft = snapToGrid(newLeft);\n const snappedTop = snapToGrid(newTop);\n\n const newPosition = { left: snappedLeft, top: snappedTop };\n\n // Update the store with the new snapped position\n this.updatePosition(\n this.currentDragItem.uuid,\n this.currentDragItem.type,\n newPosition\n );\n\n // Update canvas positions for nodes\n if (this.currentDragItem.type === 'node') {\n getStore()\n .getState()\n .updateCanvasPositions({\n [this.currentDragItem.uuid]: newPosition\n });\n }\n\n // Repaint connections if this is a node\n if (this.currentDragItem.type === 'node' && this.plumber) {\n this.plumber.repaintEverything();\n }\n }\n\n // Reset all drag state\n this.isDragging = false;\n this.isMouseDown = false;\n this.currentDragItem = null;\n }\n\n private updateCanvasSize(): void {\n if (!this.definition) return;\n\n const store = getStore();\n if (!store) return;\n\n // Calculate required canvas size based on all elements\n let maxWidth = 0;\n let maxHeight = 0;\n\n // Check node positions\n this.definition.nodes.forEach((node) => {\n const ui = this.definition._ui.nodes[node.uuid];\n if (ui && ui.position) {\n const nodeElement = this.querySelector(`[id=\"${node.uuid}\"]`);\n if (nodeElement) {\n const rect = nodeElement.getBoundingClientRect();\n maxWidth = Math.max(maxWidth, ui.position.left + rect.width);\n maxHeight = Math.max(maxHeight, ui.position.top + rect.height);\n }\n }\n });\n\n // Check sticky note positions\n const stickies = this.definition._ui?.stickies || {};\n Object.values(stickies).forEach((sticky) => {\n if (sticky.position) {\n maxWidth = Math.max(maxWidth, sticky.position.left + 200); // Sticky note width\n maxHeight = Math.max(maxHeight, sticky.position.top + 100); // Sticky note height\n }\n });\n\n // Update canvas size in store\n store.getState().expandCanvas(maxWidth, maxHeight);\n }\n\n public render(): TemplateResult {\n // we have to embed our own style since we are in light DOM\n const style = html`<style>\n ${unsafeCSS(Editor.styles.cssText)}\n ${unsafeCSS(EditorNode.styles.cssText)}\n </style>`;\n\n const stickies = this.definition?._ui?.stickies || {};\n\n return html`${style}\n <div id=\"editor\">\n <div\n id=\"grid\"\n style=\"min-width:100%;width:${this.canvasSize.width}px; height:${this\n .canvasSize.height}px\"\n >\n <div id=\"canvas\">\n ${this.definition\n ? this.definition.nodes.map((node) => {\n const position =\n this.definition._ui.nodes[node.uuid].position;\n\n const dragging =\n this.isDragging && this.currentDragItem?.uuid === node.uuid;\n\n return html`<temba-flow-node\n class=\"draggable ${dragging ? 'dragging' : ''}\"\n @mousedown=${this.handleMouseDown.bind(this)}\n uuid=${node.uuid}\n style=\"left:${position.left}px; top:${position.top}px\"\n .plumber=${this.plumber}\n .node=${node}\n .ui=${this.definition._ui.nodes[node.uuid]}\n ></temba-flow-node>`;\n })\n : html`<temba-loading></temba-loading>`}\n ${Object.entries(stickies).map(([uuid, sticky]) => {\n const position = sticky.position || { left: 0, top: 0 };\n const dragging =\n this.isDragging && this.currentDragItem?.uuid === uuid;\n return html`<temba-sticky-note\n class=\"draggable ${dragging ? 'dragging' : ''}\"\n @mousedown=${this.handleMouseDown.bind(this)}\n style=\"left:${position.left}px; top:${position.top}px; z-index: ${1000 +\n position.top}\"\n uuid=${uuid}\n .data=${sticky}\n .dragging=${dragging}\n ></temba-sticky-note>`;\n })}\n </div>\n </div>\n </div>`;\n }\n}\n"]}
@@ -6,29 +6,13 @@ import { RapidElement } from '../RapidElement';
6
6
  import { getClasses } from '../utils';
7
7
  import { getStore } from '../store/Store';
8
8
  export class EditorNode extends RapidElement {
9
- constructor() {
10
- super(...arguments);
11
- // Drag state properties
12
- this.isDragging = false;
13
- this.dragStartPos = { x: 0, y: 0 };
14
- this.nodeStartPos = { left: 0, top: 0 };
15
- // Bound event handlers to maintain proper 'this' context
16
- this.boundMouseMove = this.handleMouseMove.bind(this);
17
- this.boundMouseUp = this.handleMouseUp.bind(this);
18
- }
19
9
  createRenderRoot() {
20
10
  return this;
21
11
  }
22
- /**
23
- * Snaps a coordinate value to the nearest 20px grid position
24
- */
25
- snapToGrid(value) {
26
- return Math.round(value / 20) * 20;
27
- }
28
12
  static get styles() {
29
13
  return css `
14
+
30
15
  .node {
31
- position: absolute;
32
16
  background-color: #fff;
33
17
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
34
18
  min-width: 200px;
@@ -37,7 +21,7 @@ export class EditorNode extends RapidElement {
37
21
  color: #333;
38
22
  cursor: move;
39
23
  user-select: none;
40
- z-index: 500;
24
+
41
25
  }
42
26
 
43
27
  .node:hover {
@@ -52,21 +36,59 @@ export class EditorNode extends RapidElement {
52
36
 
53
37
  .action {
54
38
  max-width: 200px;
39
+ position: relative;
40
+ }
41
+
42
+ .action.sortable {
43
+ display: flex;
44
+ align-items: stretch;
45
+ }
46
+
47
+ .action .action-content {
48
+ flex-grow: 1;
49
+ display: flex;
50
+ flex-direction: column;
55
51
  }
56
52
 
57
53
  .action .body {
58
54
  padding: 1em;
59
55
  }
60
56
 
57
+ .action .drag-handle {
58
+ opacity: 0;
59
+ transition: all 200ms ease-in-out;
60
+ cursor: move;
61
+ background: rgba(0, 0, 0, 0.02);
62
+ max-width:0px;
63
+ position: absolute;
64
+ }
65
+
66
+ .action:hover .drag-handle {
67
+ opacity: 0.5;
68
+ padding: 0.25em;
69
+ max-width: 20px;
70
+ }
71
+
72
+ .action .drag-handle:hover {
73
+ opacity: 1;
74
+
75
+ }
76
+
61
77
  .action .title,
62
78
  .router .title {
79
+ display: flex;
63
80
  color: #fff;
64
81
  padding: 5px 1px;
65
82
  text-align: center;
66
83
  font-size: 1em;
67
84
  font-weight: normal;
85
+
68
86
  }
69
87
 
88
+ .title .name {
89
+ flex-grow: 1;
90
+ }
91
+
70
92
  .quick-replies {
71
93
  margin-top: 0.5em;
72
94
  }
@@ -120,147 +142,74 @@ export class EditorNode extends RapidElement {
120
142
  }
121
143
  }`;
122
144
  }
145
+ constructor() {
146
+ super();
147
+ this.handleActionOrderChanged = this.handleActionOrderChanged.bind(this);
148
+ }
123
149
  updated(changes) {
150
+ var _a;
124
151
  super.updated(changes);
125
152
  if (changes.has('node')) {
126
- this.plumber.makeTarget(this.node.uuid);
127
- // our node was changed, see if we have new destinations
128
- for (const exit of this.node.exits) {
129
- if (!exit.destination_uuid) {
130
- this.plumber.makeSource(exit.uuid);
131
- }
132
- else {
133
- this.plumber.connectIds(exit.uuid, exit.destination_uuid);
153
+ // make our initial connections
154
+ if (changes.get('node') === undefined) {
155
+ // this.plumber.makeTarget(this.node.uuid);
156
+ for (const exit of this.node.exits) {
157
+ if (!exit.destination_uuid) {
158
+ this.plumber.makeSource(exit.uuid);
159
+ }
160
+ else {
161
+ this.plumber.connectIds(exit.uuid, exit.destination_uuid);
162
+ }
134
163
  }
135
164
  }
136
- const ele = this.querySelector('.node');
165
+ const ele = this.parentElement;
137
166
  const rect = ele.getBoundingClientRect();
138
- getStore()
139
- .getState()
140
- .expandCanvas(this.ui.position.left + rect.width, this.ui.position.top + rect.height);
141
- // Add drag event listeners to the node
142
- this.addDragEventListeners();
143
- }
144
- }
145
- addDragEventListeners() {
146
- const nodeElement = this.querySelector('.node');
147
- if (!nodeElement)
148
- return;
149
- nodeElement.addEventListener('mousedown', this.handleMouseDown.bind(this));
150
- document.addEventListener('mousemove', this.boundMouseMove);
151
- document.addEventListener('mouseup', this.boundMouseUp);
152
- }
153
- handleMouseDown(event) {
154
- // Only start dragging if clicking on the node itself, not on exits or other interactive elements
155
- const target = event.target;
156
- if (target.classList.contains('exit') || target.closest('.exit')) {
157
- return;
158
- }
159
- this.isDragging = true;
160
- this.dragStartPos = { x: event.clientX, y: event.clientY };
161
- this.nodeStartPos = {
162
- left: this.ui.position.left,
163
- top: this.ui.position.top
164
- };
165
- // Add dragging class for visual feedback
166
- const nodeElement = this.querySelector('.node');
167
- if (nodeElement) {
168
- nodeElement.classList.add('dragging');
169
- }
170
- // Elevate connections for this node during dragging
171
- if (this.plumber) {
172
- this.plumber.elevateNodeConnections(this.node.uuid);
167
+ (_a = getStore()) === null || _a === void 0 ? void 0 : _a.getState().expandCanvas(this.ui.position.left + rect.width, this.ui.position.top + rect.height);
173
168
  }
174
- event.preventDefault();
175
- event.stopPropagation();
176
169
  }
177
- handleMouseMove(event) {
178
- if (!this.isDragging)
179
- return;
180
- const deltaX = event.clientX - this.dragStartPos.x;
181
- const deltaY = event.clientY - this.dragStartPos.y;
182
- const newLeft = this.nodeStartPos.left + deltaX;
183
- const newTop = this.nodeStartPos.top + deltaY;
184
- // Snap to 20px grid
185
- const snappedLeft = this.snapToGrid(newLeft);
186
- const snappedTop = this.snapToGrid(newTop);
187
- // Update the UI position temporarily (for visual feedback)
188
- const nodeElement = this.querySelector('.node');
189
- if (nodeElement) {
190
- nodeElement.style.left = `${snappedLeft}px`;
191
- nodeElement.style.top = `${snappedTop}px`;
192
- }
193
- // Repaint connections during dragging for smooth updates
194
- if (this.plumber) {
195
- this.plumber.repaintEverything();
196
- }
197
- }
198
- handleMouseUp(event) {
199
- if (!this.isDragging)
200
- return;
201
- this.isDragging = false;
202
- // Remove dragging class
203
- const nodeElement = this.querySelector('.node');
204
- if (nodeElement) {
205
- nodeElement.classList.remove('dragging');
206
- }
207
- // Restore normal z-index for connections
208
- if (this.plumber) {
209
- this.plumber.restoreNodeConnections(this.node.uuid);
210
- }
211
- const deltaX = event.clientX - this.dragStartPos.x;
212
- const deltaY = event.clientY - this.dragStartPos.y;
213
- const newLeft = this.nodeStartPos.left + deltaX;
214
- const newTop = this.nodeStartPos.top + deltaY;
215
- // Snap to 20px grid for final position
216
- const snappedLeft = this.snapToGrid(newLeft);
217
- const snappedTop = this.snapToGrid(newTop);
218
- // Update the store with the new snapped position
219
- const newPosition = { left: snappedLeft, top: snappedTop };
220
- getStore()
221
- .getState()
222
- .updateCanvasPositions({
223
- [this.node.uuid]: newPosition
224
- });
225
- // Repaint connections if plumber is available
226
- if (this.plumber) {
227
- this.plumber.repaintEverything();
228
- }
229
- getStore().getState().updateNodePosition(this.node.uuid, newPosition);
230
- // Fire a custom event with the new coordinates
231
- /*this.fireCustomEvent(CustomEventType.Moved, {
232
- nodeId: this.node.uuid,
233
- position: newPosition,
234
- oldPosition: {
235
- left: this.nodeStartPos.left,
236
- top: this.nodeStartPos.top
237
- }
238
- });*/
239
- }
240
- disconnectedCallback() {
241
- super.disconnectedCallback();
242
- // Clean up event listeners
243
- document.removeEventListener('mousemove', this.boundMouseMove);
244
- document.removeEventListener('mouseup', this.boundMouseUp);
170
+ handleActionOrderChanged(event) {
171
+ var _a;
172
+ const [fromIdx, toIdx] = event.detail.swap;
173
+ // swap our actions
174
+ const newActions = [...this.node.actions];
175
+ const movedAction = newActions.splice(fromIdx, 1)[0];
176
+ newActions.splice(toIdx, 0, movedAction);
177
+ // udate our internal reprensentation, this isn't strictly necessary
178
+ // since the editor will update us from it's definition subscription
179
+ // but it makes testing a lot easier
180
+ this.node = { ...this.node, actions: newActions };
181
+ (_a = getStore()) === null || _a === void 0 ? void 0 : _a.getState().updateNode(this.node.uuid, { ...this.node, actions: newActions });
245
182
  }
246
183
  renderTitle(config) {
184
+ var _a, _b;
247
185
  return html `<div class="title" style="background:${config.color}">
248
- ${config.name}
186
+ ${((_b = (_a = this.node) === null || _a === void 0 ? void 0 : _a.actions) === null || _b === void 0 ? void 0 : _b.length) > 1
187
+ ? html `<temba-icon class="drag-handle" name="sort"></temba-icon>`
188
+ : null}
189
+
190
+ <div class="name">${config.name}</div>
249
191
  </div>`;
250
192
  }
251
- renderAction(node, action) {
193
+ renderAction(node, action, index) {
252
194
  const config = EDITOR_CONFIG[action.type];
253
195
  if (config) {
254
- return html `<div class="action ${action.type}">
255
- ${this.renderTitle(config)}
256
- <div class="body">
257
- ${config.render
196
+ return html `<div
197
+ class="action sortable ${action.type}"
198
+ id="action-${index}"
199
+ >
200
+ <div class="action-content">
201
+ ${this.renderTitle(config)}
202
+ <div class="body">
203
+ ${config.render
258
204
  ? config.render(node, action)
259
205
  : html `<pre>${action.type}</pre>`}
206
+ </div>
260
207
  </div>
261
208
  </div>`;
262
209
  }
263
- return html `<div>${action.type}</div>`;
210
+ return html `<div class="action sortable" id="action-${index}">
211
+ ${action.type}
212
+ </div>`;
264
213
  }
265
214
  renderRouter(router, ui) {
266
215
  const config = EDITOR_CONFIG[ui.type];
@@ -308,9 +257,16 @@ export class EditorNode extends RapidElement {
308
257
  class="node"
309
258
  style="left:${this.ui.position.left}px;top:${this.ui.position.top}px"
310
259
  >
311
- ${this.node.actions.map((actionSpec) => {
312
- return this.renderAction(this.node, actionSpec);
313
- })}
260
+ ${this.node.actions.length > 0
261
+ ? html `<temba-sortable-list
262
+ dragHandle="drag-handle"
263
+ @temba-order-changed="${this.handleActionOrderChanged}"
264
+ >
265
+ ${this.node.actions.map((actionSpec, index) => {
266
+ return this.renderAction(this.node, actionSpec, index);
267
+ })}
268
+ </temba-sortable-list>`
269
+ : ''}
314
270
  ${this.node.router
315
271
  ? html ` ${this.renderRouter(this.node.router, this.ui)}
316
272
  ${this.renderCategories(this.node)}`
@@ -1 +1 @@
1
- {"version":3,"file":"EditorNode.js","sourceRoot":"","sources":["../../../src/flow/EditorNode.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { EDITOR_CONFIG, UIConfig } from './config';\nimport { Action, Exit, Node, NodeUI, Router } from '../store/flow-definition';\nimport { property } from 'lit/decorators.js';\nimport { RapidElement } from '../RapidElement';\nimport { getClasses } from '../utils';\nimport { Plumber } from './Plumber';\nimport { getStore } from '../store/Store';\n\nexport class EditorNode extends RapidElement {\n createRenderRoot() {\n return this;\n }\n\n @property({ type: Object })\n private plumber: Plumber;\n\n @property({ type: Object })\n private node: Node;\n\n @property({ type: Object })\n private ui: NodeUI;\n\n // Drag state properties\n private isDragging = false;\n private dragStartPos = { x: 0, y: 0 };\n private nodeStartPos = { left: 0, top: 0 };\n\n // Bound event handlers to maintain proper 'this' context\n private boundMouseMove = this.handleMouseMove.bind(this);\n private boundMouseUp = this.handleMouseUp.bind(this);\n\n /**\n * Snaps a coordinate value to the nearest 20px grid position\n */\n private snapToGrid(value: number): number {\n return Math.round(value / 20) * 20;\n }\n\n static get styles() {\n return css`\n .node {\n position: absolute;\n background-color: #fff;\n box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);\n min-width: 200px;\n border-radius: calc(var(--curvature) * 1.5);\n overflow: hidden;\n color: #333;\n cursor: move;\n user-select: none;\n z-index: 500;\n }\n\n .node:hover {\n box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);\n }\n\n .node.dragging {\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4);\n transform: scale(1.02);\n z-index: 1000;\n }\n \n .action {\n max-width: 200px;\n }\n\n .action .body {\n padding: 1em;\n }\n\n .action .title,\n .router .title {\n color: #fff;\n padding: 5px 1px;\n text-align: center;\n font-size: 1em;\n font-weight: normal;\n }\n\n .quick-replies {\n margin-top: 0.5em;\n }\n\n .quick-reply {\n background-color: #f0f0f0;\n border: 1px solid #e0e0e0;\n border-radius: calc(var(--curvature) * 1.5);\n padding: 0.2em 1em;\n display: inline-block;\n font-size: 0.8em;\n margin: 0.2em;\n }\n\n .categories {\n display: flex;\n flex-direction: row;\n\n }\n\n .category {\n margin:-1px -0.5px;\n border: 1px solid #f3f3f3;\n padding: 0.75em;\n flex-grow:1;\n text-align: center;\n }\n\n .action-exits {\n padding-bottom: 0.75em;\n margin-top: -0.75em;\n }\n\n .category .title {\n font-weight: normal;\n font-size: 1em;\n }\n\n .router .body {\n padding: 0.75em;\n }\n\n .result-name {\n font-weight: bold;\n display: inline-block;\n }\n \n .exit {\n padding-top: 10px;\n margin-bottom: -10px;\n }\n }`;\n }\n\n protected updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n if (changes.has('node')) {\n this.plumber.makeTarget(this.node.uuid);\n\n // our node was changed, see if we have new destinations\n for (const exit of this.node.exits) {\n if (!exit.destination_uuid) {\n this.plumber.makeSource(exit.uuid);\n } else {\n this.plumber.connectIds(exit.uuid, exit.destination_uuid);\n }\n }\n\n const ele = this.querySelector('.node');\n const rect = ele.getBoundingClientRect();\n\n getStore()\n .getState()\n .expandCanvas(\n this.ui.position.left + rect.width,\n this.ui.position.top + rect.height\n );\n\n // Add drag event listeners to the node\n this.addDragEventListeners();\n }\n }\n\n private addDragEventListeners(): void {\n const nodeElement = this.querySelector('.node') as HTMLElement;\n if (!nodeElement) return;\n\n nodeElement.addEventListener('mousedown', this.handleMouseDown.bind(this));\n document.addEventListener('mousemove', this.boundMouseMove);\n document.addEventListener('mouseup', this.boundMouseUp);\n }\n\n private handleMouseDown(event: MouseEvent): void {\n // Only start dragging if clicking on the node itself, not on exits or other interactive elements\n const target = event.target as HTMLElement;\n if (target.classList.contains('exit') || target.closest('.exit')) {\n return;\n }\n\n this.isDragging = true;\n this.dragStartPos = { x: event.clientX, y: event.clientY };\n this.nodeStartPos = {\n left: this.ui.position.left,\n top: this.ui.position.top\n };\n\n // Add dragging class for visual feedback\n const nodeElement = this.querySelector('.node') as HTMLElement;\n if (nodeElement) {\n nodeElement.classList.add('dragging');\n }\n\n // Elevate connections for this node during dragging\n if (this.plumber) {\n this.plumber.elevateNodeConnections(this.node.uuid);\n }\n\n event.preventDefault();\n event.stopPropagation();\n }\n\n private handleMouseMove(event: MouseEvent): void {\n if (!this.isDragging) return;\n\n const deltaX = event.clientX - this.dragStartPos.x;\n const deltaY = event.clientY - this.dragStartPos.y;\n\n const newLeft = this.nodeStartPos.left + deltaX;\n const newTop = this.nodeStartPos.top + deltaY;\n\n // Snap to 20px grid\n const snappedLeft = this.snapToGrid(newLeft);\n const snappedTop = this.snapToGrid(newTop);\n\n // Update the UI position temporarily (for visual feedback)\n const nodeElement = this.querySelector('.node') as HTMLElement;\n if (nodeElement) {\n nodeElement.style.left = `${snappedLeft}px`;\n nodeElement.style.top = `${snappedTop}px`;\n }\n\n // Repaint connections during dragging for smooth updates\n if (this.plumber) {\n this.plumber.repaintEverything();\n }\n }\n\n private handleMouseUp(event: MouseEvent): void {\n if (!this.isDragging) return;\n\n this.isDragging = false;\n\n // Remove dragging class\n const nodeElement = this.querySelector('.node') as HTMLElement;\n if (nodeElement) {\n nodeElement.classList.remove('dragging');\n }\n\n // Restore normal z-index for connections\n if (this.plumber) {\n this.plumber.restoreNodeConnections(this.node.uuid);\n }\n\n const deltaX = event.clientX - this.dragStartPos.x;\n const deltaY = event.clientY - this.dragStartPos.y;\n\n const newLeft = this.nodeStartPos.left + deltaX;\n const newTop = this.nodeStartPos.top + deltaY;\n\n // Snap to 20px grid for final position\n const snappedLeft = this.snapToGrid(newLeft);\n const snappedTop = this.snapToGrid(newTop);\n\n // Update the store with the new snapped position\n const newPosition = { left: snappedLeft, top: snappedTop };\n getStore()\n .getState()\n .updateCanvasPositions({\n [this.node.uuid]: newPosition\n });\n\n // Repaint connections if plumber is available\n if (this.plumber) {\n this.plumber.repaintEverything();\n }\n\n getStore().getState().updateNodePosition(this.node.uuid, newPosition);\n\n // Fire a custom event with the new coordinates\n /*this.fireCustomEvent(CustomEventType.Moved, {\n nodeId: this.node.uuid,\n position: newPosition,\n oldPosition: {\n left: this.nodeStartPos.left,\n top: this.nodeStartPos.top\n }\n });*/\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n // Clean up event listeners\n document.removeEventListener('mousemove', this.boundMouseMove);\n document.removeEventListener('mouseup', this.boundMouseUp);\n }\n\n private renderTitle(config: UIConfig) {\n return html`<div class=\"title\" style=\"background:${config.color}\">\n ${config.name}\n </div>`;\n }\n\n private renderAction(node: Node, action: Action) {\n const config = EDITOR_CONFIG[action.type];\n\n if (config) {\n return html`<div class=\"action ${action.type}\">\n ${this.renderTitle(config)}\n <div class=\"body\">\n ${config.render\n ? config.render(node, action)\n : html`<pre>${action.type}</pre>`}\n </div>\n </div>`;\n }\n return html`<div>${action.type}</div>`;\n }\n\n private renderRouter(router: Router, ui: NodeUI) {\n const config = EDITOR_CONFIG[ui.type];\n if (config) {\n return html`<div class=\"router\">\n ${this.renderTitle(config)}\n ${router.result_name\n ? html`<div class=\"body\">\n Save as\n <div class=\"result-name\">${router.result_name}</div>\n </div>`\n : null}\n </div>`;\n }\n }\n\n private renderCategories(node: Node) {\n if (!node.router || !node.router.categories) {\n return null;\n }\n const categories = node.router.categories.map((category) => {\n const exit = node.exits.find(\n (exit: Exit) => exit.uuid == category.exit_uuid\n );\n\n return html`<div class=\"category\">\n <div class=\"title\">${category.name}</div>\n ${this.renderExit(exit)}\n </div>`;\n });\n\n return html`<div class=\"categories\">${categories}</div>`;\n }\n\n private renderExit(exit: Exit): TemplateResult {\n return html`<div\n id=\"${exit.uuid}\"\n class=${getClasses({\n exit: true,\n connected: !!exit.destination_uuid\n })}\n ></div>`;\n }\n\n public render() {\n if (!this.node || !this.ui) {\n return html`<div class=\"node\">Loading...</div>`;\n }\n\n return html`\n <div\n id=\"${this.node.uuid}\"\n class=\"node\"\n style=\"left:${this.ui.position.left}px;top:${this.ui.position.top}px\"\n >\n ${this.node.actions.map((actionSpec) => {\n return this.renderAction(this.node, actionSpec);\n })}\n ${this.node.router\n ? html` ${this.renderRouter(this.node.router, this.ui)}\n ${this.renderCategories(this.node)}`\n : html`<div class=\"action-exits\">\n ${this.node.exits.map((exit) => {\n return this.renderExit(exit);\n })}\n </div>`}\n </div>\n `;\n }\n}\n"]}
1
+ {"version":3,"file":"EditorNode.js","sourceRoot":"","sources":["../../../src/flow/EditorNode.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { EDITOR_CONFIG, UIConfig } from './config';\nimport { Action, Exit, Node, NodeUI, Router } from '../store/flow-definition';\nimport { property } from 'lit/decorators.js';\nimport { RapidElement } from '../RapidElement';\nimport { getClasses } from '../utils';\nimport { Plumber } from './Plumber';\nimport { getStore } from '../store/Store';\n\nexport class EditorNode extends RapidElement {\n createRenderRoot() {\n return this;\n }\n\n @property({ type: Object })\n private plumber: Plumber;\n\n @property({ type: Object })\n private node: Node;\n\n @property({ type: Object })\n private ui: NodeUI;\n\n static get styles() {\n return css`\n\n .node {\n background-color: #fff;\n box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);\n min-width: 200px;\n border-radius: calc(var(--curvature) * 1.5);\n overflow: hidden;\n color: #333;\n cursor: move;\n user-select: none;\n\n }\n\n .node:hover {\n box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);\n }\n\n .node.dragging {\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4);\n transform: scale(1.02);\n z-index: 1000;\n }\n \n .action {\n max-width: 200px;\n position: relative;\n }\n\n .action.sortable {\n display: flex;\n align-items: stretch;\n }\n\n .action .action-content {\n flex-grow: 1;\n display: flex;\n flex-direction: column;\n }\n\n .action .body {\n padding: 1em;\n }\n\n .action .drag-handle {\n opacity: 0;\n transition: all 200ms ease-in-out;\n cursor: move;\n background: rgba(0, 0, 0, 0.02);\n max-width:0px;\n position: absolute;\n }\n\n .action:hover .drag-handle {\n opacity: 0.5;\n padding: 0.25em;\n max-width: 20px;\n }\n\n .action .drag-handle:hover {\n opacity: 1;\n \n }\n\n .action .title,\n .router .title {\n display: flex;\n color: #fff;\n padding: 5px 1px;\n text-align: center;\n font-size: 1em;\n font-weight: normal;\n\n }\n\n .title .name {\n flex-grow: 1;\n }\n\n .quick-replies {\n margin-top: 0.5em;\n }\n\n .quick-reply {\n background-color: #f0f0f0;\n border: 1px solid #e0e0e0;\n border-radius: calc(var(--curvature) * 1.5);\n padding: 0.2em 1em;\n display: inline-block;\n font-size: 0.8em;\n margin: 0.2em;\n }\n\n .categories {\n display: flex;\n flex-direction: row;\n\n }\n\n .category {\n margin:-1px -0.5px;\n border: 1px solid #f3f3f3;\n padding: 0.75em;\n flex-grow:1;\n text-align: center;\n }\n\n .action-exits {\n padding-bottom: 0.75em;\n margin-top: -0.75em;\n }\n\n .category .title {\n font-weight: normal;\n font-size: 1em;\n }\n\n .router .body {\n padding: 0.75em;\n }\n\n .result-name {\n font-weight: bold;\n display: inline-block;\n }\n \n .exit {\n padding-top: 10px;\n margin-bottom: -10px;\n }\n }`;\n }\n\n constructor() {\n super();\n this.handleActionOrderChanged = this.handleActionOrderChanged.bind(this);\n }\n\n protected updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n if (changes.has('node')) {\n // make our initial connections\n if (changes.get('node') === undefined) {\n // this.plumber.makeTarget(this.node.uuid);\n for (const exit of this.node.exits) {\n if (!exit.destination_uuid) {\n this.plumber.makeSource(exit.uuid);\n } else {\n this.plumber.connectIds(exit.uuid, exit.destination_uuid);\n }\n }\n }\n\n const ele = this.parentElement;\n const rect = ele.getBoundingClientRect();\n\n getStore()\n ?.getState()\n .expandCanvas(\n this.ui.position.left + rect.width,\n this.ui.position.top + rect.height\n );\n }\n }\n\n private handleActionOrderChanged(event: CustomEvent) {\n const [fromIdx, toIdx] = event.detail.swap;\n\n // swap our actions\n const newActions = [...this.node.actions];\n const movedAction = newActions.splice(fromIdx, 1)[0];\n newActions.splice(toIdx, 0, movedAction);\n\n // udate our internal reprensentation, this isn't strictly necessary\n // since the editor will update us from it's definition subscription\n // but it makes testing a lot easier\n this.node = { ...this.node, actions: newActions };\n\n getStore()\n ?.getState()\n .updateNode(this.node.uuid, { ...this.node, actions: newActions });\n }\n\n private renderTitle(config: UIConfig) {\n return html`<div class=\"title\" style=\"background:${config.color}\">\n ${this.node?.actions?.length > 1\n ? html`<temba-icon class=\"drag-handle\" name=\"sort\"></temba-icon>`\n : null}\n\n <div class=\"name\">${config.name}</div>\n </div>`;\n }\n\n private renderAction(node: Node, action: Action, index: number) {\n const config = EDITOR_CONFIG[action.type];\n\n if (config) {\n return html`<div\n class=\"action sortable ${action.type}\"\n id=\"action-${index}\"\n >\n <div class=\"action-content\">\n ${this.renderTitle(config)}\n <div class=\"body\">\n ${config.render\n ? config.render(node, action)\n : html`<pre>${action.type}</pre>`}\n </div>\n </div>\n </div>`;\n }\n return html`<div class=\"action sortable\" id=\"action-${index}\">\n ${action.type}\n </div>`;\n }\n\n private renderRouter(router: Router, ui: NodeUI) {\n const config = EDITOR_CONFIG[ui.type];\n if (config) {\n return html`<div class=\"router\">\n ${this.renderTitle(config)}\n ${router.result_name\n ? html`<div class=\"body\">\n Save as\n <div class=\"result-name\">${router.result_name}</div>\n </div>`\n : null}\n </div>`;\n }\n }\n\n private renderCategories(node: Node) {\n if (!node.router || !node.router.categories) {\n return null;\n }\n const categories = node.router.categories.map((category) => {\n const exit = node.exits.find(\n (exit: Exit) => exit.uuid == category.exit_uuid\n );\n\n return html`<div class=\"category\">\n <div class=\"title\">${category.name}</div>\n ${this.renderExit(exit)}\n </div>`;\n });\n\n return html`<div class=\"categories\">${categories}</div>`;\n }\n\n private renderExit(exit: Exit): TemplateResult {\n return html`<div\n id=\"${exit.uuid}\"\n class=${getClasses({\n exit: true,\n connected: !!exit.destination_uuid\n })}\n ></div>`;\n }\n\n public render() {\n if (!this.node || !this.ui) {\n return html`<div class=\"node\">Loading...</div>`;\n }\n\n return html`\n <div\n id=\"${this.node.uuid}\"\n class=\"node\"\n style=\"left:${this.ui.position.left}px;top:${this.ui.position.top}px\"\n >\n ${this.node.actions.length > 0\n ? html`<temba-sortable-list\n dragHandle=\"drag-handle\"\n @temba-order-changed=\"${this.handleActionOrderChanged}\"\n >\n ${this.node.actions.map((actionSpec, index) => {\n return this.renderAction(this.node, actionSpec, index);\n })}\n </temba-sortable-list>`\n : ''}\n ${this.node.router\n ? html` ${this.renderRouter(this.node.router, this.ui)}\n ${this.renderCategories(this.node)}`\n : html`<div class=\"action-exits\">\n ${this.node.exits.map((exit) => {\n return this.renderExit(exit);\n })}\n </div>`}\n </div>\n `;\n }\n}\n"]}