@nyaruka/temba-components 0.131.2 → 0.131.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 (223) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/demo/components/floating-tabs/example.html +400 -0
  3. package/demo/components/flow/index.html +1 -1
  4. package/demo/data/flows/sample-flow.json +41 -2
  5. package/demo/data/flows/voicemail.json +613 -0
  6. package/demo/index.html +6 -0
  7. package/dist/locales/es.js +5 -5
  8. package/dist/locales/es.js.map +1 -1
  9. package/dist/locales/fr.js +5 -5
  10. package/dist/locales/fr.js.map +1 -1
  11. package/dist/locales/locale-codes.js +11 -2
  12. package/dist/locales/locale-codes.js.map +1 -1
  13. package/dist/locales/pt.js +5 -5
  14. package/dist/locales/pt.js.map +1 -1
  15. package/dist/temba-components.js +1109 -535
  16. package/dist/temba-components.js.map +1 -1
  17. package/out-tsc/src/display/FloatingTab.js +167 -0
  18. package/out-tsc/src/display/FloatingTab.js.map +1 -0
  19. package/out-tsc/src/display/ProgressBar.js +22 -2
  20. package/out-tsc/src/display/ProgressBar.js.map +1 -1
  21. package/out-tsc/src/events.js.map +1 -1
  22. package/out-tsc/src/flow/CanvasNode.js +165 -31
  23. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  24. package/out-tsc/src/flow/Editor.js +857 -3
  25. package/out-tsc/src/flow/Editor.js.map +1 -1
  26. package/out-tsc/src/flow/NodeEditor.js +239 -19
  27. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  28. package/out-tsc/src/flow/NodeTypeSelector.js +44 -3
  29. package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -1
  30. package/out-tsc/src/flow/StickyNote.js +12 -3
  31. package/out-tsc/src/flow/StickyNote.js.map +1 -1
  32. package/out-tsc/src/flow/actions/add_contact_groups.js +2 -1
  33. package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
  34. package/out-tsc/src/flow/actions/add_contact_urn.js +2 -1
  35. package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
  36. package/out-tsc/src/flow/actions/add_input_labels.js +2 -1
  37. package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
  38. package/out-tsc/src/flow/actions/play_audio.js +2 -1
  39. package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
  40. package/out-tsc/src/flow/actions/remove_contact_groups.js +2 -1
  41. package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
  42. package/out-tsc/src/flow/actions/request_optin.js +1 -0
  43. package/out-tsc/src/flow/actions/request_optin.js.map +1 -1
  44. package/out-tsc/src/flow/actions/say_msg.js +2 -1
  45. package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
  46. package/out-tsc/src/flow/actions/send_broadcast.js +2 -1
  47. package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
  48. package/out-tsc/src/flow/actions/send_email.js +2 -1
  49. package/out-tsc/src/flow/actions/send_email.js.map +1 -1
  50. package/out-tsc/src/flow/actions/send_msg.js +93 -3
  51. package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
  52. package/out-tsc/src/flow/actions/set_contact_channel.js +2 -1
  53. package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
  54. package/out-tsc/src/flow/actions/set_contact_field.js +2 -1
  55. package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
  56. package/out-tsc/src/flow/actions/set_contact_language.js +2 -1
  57. package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
  58. package/out-tsc/src/flow/actions/set_contact_name.js +2 -1
  59. package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
  60. package/out-tsc/src/flow/actions/set_contact_status.js +2 -1
  61. package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
  62. package/out-tsc/src/flow/actions/set_run_result.js +2 -1
  63. package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
  64. package/out-tsc/src/flow/actions/start_session.js +2 -1
  65. package/out-tsc/src/flow/actions/start_session.js.map +1 -1
  66. package/out-tsc/src/flow/config.js +2 -10
  67. package/out-tsc/src/flow/config.js.map +1 -1
  68. package/out-tsc/src/flow/nodes/shared.js +54 -0
  69. package/out-tsc/src/flow/nodes/shared.js.map +1 -1
  70. package/out-tsc/src/flow/nodes/split_by_airtime.js +9 -3
  71. package/out-tsc/src/flow/nodes/split_by_airtime.js.map +1 -1
  72. package/out-tsc/src/flow/nodes/split_by_contact_field.js +8 -3
  73. package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -1
  74. package/out-tsc/src/flow/nodes/split_by_expression.js +8 -3
  75. package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
  76. package/out-tsc/src/flow/nodes/split_by_groups.js +8 -3
  77. package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -1
  78. package/out-tsc/src/flow/nodes/split_by_intent.js +3 -2
  79. package/out-tsc/src/flow/nodes/split_by_intent.js.map +1 -1
  80. package/out-tsc/src/flow/nodes/split_by_llm.js +9 -2
  81. package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
  82. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +9 -2
  83. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
  84. package/out-tsc/src/flow/nodes/split_by_random.js +8 -2
  85. package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
  86. package/out-tsc/src/flow/nodes/split_by_resthook.js +8 -3
  87. package/out-tsc/src/flow/nodes/split_by_resthook.js.map +1 -1
  88. package/out-tsc/src/flow/nodes/split_by_run_result.js +8 -3
  89. package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -1
  90. package/out-tsc/src/flow/nodes/split_by_scheme.js +8 -3
  91. package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -1
  92. package/out-tsc/src/flow/nodes/split_by_subflow.js +8 -2
  93. package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
  94. package/out-tsc/src/flow/nodes/split_by_ticket.js +8 -2
  95. package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
  96. package/out-tsc/src/flow/nodes/split_by_webhook.js +8 -2
  97. package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
  98. package/out-tsc/src/flow/nodes/wait_for_digits.js +3 -2
  99. package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
  100. package/out-tsc/src/flow/nodes/wait_for_menu.js +3 -2
  101. package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
  102. package/out-tsc/src/flow/nodes/wait_for_response.js +8 -3
  103. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
  104. package/out-tsc/src/flow/types.js +15 -0
  105. package/out-tsc/src/flow/types.js.map +1 -1
  106. package/out-tsc/src/layout/FloatingWindow.js +346 -0
  107. package/out-tsc/src/layout/FloatingWindow.js.map +1 -0
  108. package/out-tsc/src/live/ContactChat.js +3 -19
  109. package/out-tsc/src/live/ContactChat.js.map +1 -1
  110. package/out-tsc/src/locales/es.js +5 -5
  111. package/out-tsc/src/locales/es.js.map +1 -1
  112. package/out-tsc/src/locales/fr.js +5 -5
  113. package/out-tsc/src/locales/fr.js.map +1 -1
  114. package/out-tsc/src/locales/locale-codes.js +11 -2
  115. package/out-tsc/src/locales/locale-codes.js.map +1 -1
  116. package/out-tsc/src/locales/pt.js +5 -5
  117. package/out-tsc/src/locales/pt.js.map +1 -1
  118. package/out-tsc/src/store/AppState.js +67 -0
  119. package/out-tsc/src/store/AppState.js.map +1 -1
  120. package/out-tsc/temba-modules.js +4 -0
  121. package/out-tsc/temba-modules.js.map +1 -1
  122. package/out-tsc/test/temba-floating-tab.test.js +91 -0
  123. package/out-tsc/test/temba-floating-tab.test.js.map +1 -0
  124. package/out-tsc/test/temba-floating-window.test.js +301 -0
  125. package/out-tsc/test/temba-floating-window.test.js.map +1 -0
  126. package/out-tsc/test/temba-flow-editor-node.test.js +117 -0
  127. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
  128. package/out-tsc/test/temba-localization.test.js +471 -0
  129. package/out-tsc/test/temba-localization.test.js.map +1 -0
  130. package/out-tsc/test/temba-node-type-selector.test.js +150 -0
  131. package/out-tsc/test/temba-node-type-selector.test.js.map +1 -1
  132. package/out-tsc/test/utils.test.js +18 -0
  133. package/out-tsc/test/utils.test.js.map +1 -1
  134. package/package.json +1 -1
  135. package/screenshots/truth/floating-tab/default.png +0 -0
  136. package/screenshots/truth/floating-tab/gray.png +0 -0
  137. package/screenshots/truth/floating-tab/green.png +0 -0
  138. package/screenshots/truth/floating-tab/hidden.png +0 -0
  139. package/screenshots/truth/floating-tab/hover.png +0 -0
  140. package/screenshots/truth/floating-tab/purple.png +0 -0
  141. package/screenshots/truth/floating-window/chromeless.png +0 -0
  142. package/screenshots/truth/floating-window/custom-size.png +0 -0
  143. package/screenshots/truth/floating-window/default.png +0 -0
  144. package/screenshots/truth/floating-window/with-header.png +0 -0
  145. package/screenshots/truth/node-type-selector/action-mode.png +0 -0
  146. package/screenshots/truth/node-type-selector/split-mode.png +0 -0
  147. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  148. package/src/display/FloatingTab.ts +174 -0
  149. package/src/display/ProgressBar.ts +22 -2
  150. package/src/events.ts +2 -4
  151. package/src/flow/CanvasNode.ts +190 -32
  152. package/src/flow/Editor.ts +1040 -3
  153. package/src/flow/NodeEditor.ts +317 -19
  154. package/src/flow/NodeTypeSelector.ts +47 -3
  155. package/src/flow/StickyNote.ts +12 -3
  156. package/src/flow/actions/add_contact_groups.ts +2 -1
  157. package/src/flow/actions/add_contact_urn.ts +3 -1
  158. package/src/flow/actions/add_input_labels.ts +2 -1
  159. package/src/flow/actions/play_audio.ts +2 -1
  160. package/src/flow/actions/remove_contact_groups.ts +3 -1
  161. package/src/flow/actions/request_optin.ts +1 -0
  162. package/src/flow/actions/say_msg.ts +2 -1
  163. package/src/flow/actions/send_broadcast.ts +2 -1
  164. package/src/flow/actions/send_email.ts +3 -1
  165. package/src/flow/actions/send_msg.ts +134 -3
  166. package/src/flow/actions/set_contact_channel.ts +2 -1
  167. package/src/flow/actions/set_contact_field.ts +2 -1
  168. package/src/flow/actions/set_contact_language.ts +3 -1
  169. package/src/flow/actions/set_contact_name.ts +2 -1
  170. package/src/flow/actions/set_contact_status.ts +2 -1
  171. package/src/flow/actions/set_run_result.ts +2 -1
  172. package/src/flow/actions/start_session.ts +3 -1
  173. package/src/flow/config.ts +2 -12
  174. package/src/flow/nodes/shared.ts +70 -1
  175. package/src/flow/nodes/split_by_airtime.ts +20 -3
  176. package/src/flow/nodes/split_by_contact_field.ts +13 -3
  177. package/src/flow/nodes/split_by_expression.ts +13 -3
  178. package/src/flow/nodes/split_by_groups.ts +13 -3
  179. package/src/flow/nodes/split_by_intent.ts +3 -2
  180. package/src/flow/nodes/split_by_llm.ts +19 -2
  181. package/src/flow/nodes/split_by_llm_categorize.ts +19 -2
  182. package/src/flow/nodes/split_by_random.ts +12 -2
  183. package/src/flow/nodes/split_by_resthook.ts +13 -3
  184. package/src/flow/nodes/split_by_run_result.ts +13 -3
  185. package/src/flow/nodes/split_by_scheme.ts +13 -3
  186. package/src/flow/nodes/split_by_subflow.ts +12 -2
  187. package/src/flow/nodes/split_by_ticket.ts +12 -2
  188. package/src/flow/nodes/split_by_webhook.ts +12 -2
  189. package/src/flow/nodes/wait_for_digits.ts +3 -2
  190. package/src/flow/nodes/wait_for_menu.ts +3 -2
  191. package/src/flow/nodes/wait_for_response.ts +13 -3
  192. package/src/flow/types.ts +47 -0
  193. package/src/layout/FloatingWindow.ts +386 -0
  194. package/src/live/ContactChat.ts +4 -19
  195. package/src/locales/es.ts +18 -13
  196. package/src/locales/fr.ts +18 -13
  197. package/src/locales/locale-codes.ts +11 -2
  198. package/src/locales/pt.ts +18 -13
  199. package/src/store/AppState.ts +104 -0
  200. package/static/api/llms.json +18 -0
  201. package/temba-modules.ts +4 -0
  202. package/test/temba-floating-tab.test.ts +110 -0
  203. package/test/temba-floating-window.test.ts +477 -0
  204. package/test/temba-flow-editor-node.test.ts +144 -0
  205. package/test/temba-localization.test.ts +611 -0
  206. package/test/temba-node-type-selector.test.ts +203 -0
  207. package/test/utils.test.ts +20 -0
  208. package/test-assets/contacts/history.json +5 -6
  209. package/test-assets/select/llms.json +2 -2
  210. package/web-dev-server.config.mjs +47 -1
  211. package/web-test-runner.config.mjs +0 -1
  212. package/out-tsc/src/flow/nodes/wait_for_audio.js +0 -7
  213. package/out-tsc/src/flow/nodes/wait_for_audio.js.map +0 -1
  214. package/out-tsc/src/flow/nodes/wait_for_image.js +0 -7
  215. package/out-tsc/src/flow/nodes/wait_for_image.js.map +0 -1
  216. package/out-tsc/src/flow/nodes/wait_for_location.js +0 -7
  217. package/out-tsc/src/flow/nodes/wait_for_location.js.map +0 -1
  218. package/out-tsc/src/flow/nodes/wait_for_video.js +0 -7
  219. package/out-tsc/src/flow/nodes/wait_for_video.js.map +0 -1
  220. package/src/flow/nodes/wait_for_audio.ts +0 -7
  221. package/src/flow/nodes/wait_for_image.ts +0 -7
  222. package/src/flow/nodes/wait_for_location.ts +0 -7
  223. package/src/flow/nodes/wait_for_video.ts +0 -7
@@ -0,0 +1,167 @@
1
+ import { __decorate } from "tslib";
2
+ import { css, html } from 'lit';
3
+ import { property } from 'lit/decorators.js';
4
+ import { RapidElement } from '../RapidElement';
5
+ import { CustomEventType } from '../interfaces';
6
+ import { getClasses } from '../utils';
7
+ export class FloatingTab extends RapidElement {
8
+ constructor() {
9
+ super(...arguments);
10
+ this.color = '#6B7280';
11
+ this.top = -1; // -1 means auto-calculate position
12
+ this.hidden = false;
13
+ }
14
+ static get styles() {
15
+ return css `
16
+ .tab.hidden {
17
+ transform: translateX(100%);
18
+ }
19
+ .tab {
20
+ position: fixed;
21
+ right: 0;
22
+ z-index: 9998;
23
+ transition: transform var(--transition-duration, 300ms) ease-in-out;
24
+ display: flex;
25
+ align-items: center;
26
+ padding: 12px;
27
+ border-top-left-radius: 8px;
28
+ border-bottom-left-radius: 8px;
29
+ cursor: pointer;
30
+ box-shadow: -2px 2px 8px rgba(0, 0, 0, 0.2);
31
+ transition: all calc(var(--transition-duration, 300ms) * 0.7)
32
+ ease-in-out;
33
+ user-select: none;
34
+ }
35
+
36
+ .tab:hover {
37
+ padding-right: 16px;
38
+ box-shadow: -4px 4px 12px rgba(0, 0, 0, 0.3);
39
+ }
40
+
41
+ .icon-container {
42
+ display: flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ width: 32px;
46
+ height: 32px;
47
+ }
48
+
49
+ temba-icon {
50
+ --icon-color: white;
51
+ }
52
+
53
+ .label {
54
+ color: white;
55
+ font-weight: 500;
56
+ font-size: 16px;
57
+ max-width: 0;
58
+ overflow: hidden;
59
+ white-space: nowrap;
60
+ margin-left: 0;
61
+ opacity: 0;
62
+ transition: all var(--transition-duration, 300ms) ease-in-out;
63
+ }
64
+
65
+ .tab:hover .label {
66
+ max-width: 200px;
67
+ margin-left: 12px;
68
+ opacity: 1;
69
+ }
70
+ `;
71
+ }
72
+ connectedCallback() {
73
+ super.connectedCallback();
74
+ FloatingTab.allTabs.push(this);
75
+ this.updatePosition();
76
+ }
77
+ disconnectedCallback() {
78
+ super.disconnectedCallback();
79
+ const index = FloatingTab.allTabs.indexOf(this);
80
+ if (index > -1) {
81
+ FloatingTab.allTabs.splice(index, 1);
82
+ }
83
+ // update positions of remaining tabs
84
+ FloatingTab.allTabs.forEach((tab) => tab.updatePosition());
85
+ }
86
+ updatePosition() {
87
+ // if top is manually set, use it
88
+ if (this.top !== -1) {
89
+ return;
90
+ }
91
+ // auto-calculate position based on index
92
+ const index = FloatingTab.allTabs.indexOf(this);
93
+ if (index === -1) {
94
+ this.top = 100; // default fallback
95
+ }
96
+ else {
97
+ // start at 100px and stack with 10px gap between tabs
98
+ this.top = 100 + index * (FloatingTab.TAB_HEIGHT + 10);
99
+ }
100
+ }
101
+ updated(changes) {
102
+ super.updated(changes);
103
+ if (changes.has('hidden')) {
104
+ this.classList.toggle('hidden', this.hidden);
105
+ }
106
+ if (changes.has('top')) {
107
+ this.updatePosition();
108
+ }
109
+ }
110
+ handleClick() {
111
+ // hide all tabs when one is clicked
112
+ FloatingTab.allTabs.forEach((tab) => {
113
+ tab.hidden = true;
114
+ });
115
+ this.fireCustomEvent(CustomEventType.ButtonClicked, {
116
+ action: 'toggle'
117
+ });
118
+ }
119
+ static showAllTabs() {
120
+ FloatingTab.allTabs.forEach((tab) => {
121
+ tab.hidden = false;
122
+ });
123
+ }
124
+ static hideAllTabs() {
125
+ FloatingTab.allTabs.forEach((tab) => {
126
+ tab.hidden = true;
127
+ });
128
+ }
129
+ render() {
130
+ const tabStyle = `
131
+ background-color: ${this.color};
132
+ top: ${this.top}px;
133
+ `;
134
+ const classes = getClasses({
135
+ tab: true,
136
+ hidden: this.hidden
137
+ });
138
+ return html `
139
+ <div class="${classes}" style="${tabStyle}" @click=${this.handleClick}>
140
+ <div class="icon-container">
141
+ ${this.icon
142
+ ? html `<temba-icon size="2" name="${this.icon}"></temba-icon>`
143
+ : ''}
144
+ </div>
145
+ <div class="label">${this.label}</div>
146
+ </div>
147
+ `;
148
+ }
149
+ }
150
+ FloatingTab.TAB_HEIGHT = 56; // height of tab for auto-stacking
151
+ FloatingTab.allTabs = [];
152
+ __decorate([
153
+ property({ type: String })
154
+ ], FloatingTab.prototype, "icon", void 0);
155
+ __decorate([
156
+ property({ type: String })
157
+ ], FloatingTab.prototype, "label", void 0);
158
+ __decorate([
159
+ property({ type: String })
160
+ ], FloatingTab.prototype, "color", void 0);
161
+ __decorate([
162
+ property({ type: Number })
163
+ ], FloatingTab.prototype, "top", void 0);
164
+ __decorate([
165
+ property({ type: Boolean })
166
+ ], FloatingTab.prototype, "hidden", void 0);
167
+ //# sourceMappingURL=FloatingTab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FloatingTab.js","sourceRoot":"","sources":["../../../src/display/FloatingTab.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 { CustomEventType } from '../interfaces';\nimport { getClasses } from '../utils';\n\nexport class FloatingTab extends RapidElement {\n static get styles() {\n return css`\n .tab.hidden {\n transform: translateX(100%);\n }\n .tab {\n position: fixed;\n right: 0;\n z-index: 9998;\n transition: transform var(--transition-duration, 300ms) ease-in-out;\n display: flex;\n align-items: center;\n padding: 12px;\n border-top-left-radius: 8px;\n border-bottom-left-radius: 8px;\n cursor: pointer;\n box-shadow: -2px 2px 8px rgba(0, 0, 0, 0.2);\n transition: all calc(var(--transition-duration, 300ms) * 0.7)\n ease-in-out;\n user-select: none;\n }\n\n .tab:hover {\n padding-right: 16px;\n box-shadow: -4px 4px 12px rgba(0, 0, 0, 0.3);\n }\n\n .icon-container {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n }\n\n temba-icon {\n --icon-color: white;\n }\n\n .label {\n color: white;\n font-weight: 500;\n font-size: 16px;\n max-width: 0;\n overflow: hidden;\n white-space: nowrap;\n margin-left: 0;\n opacity: 0;\n transition: all var(--transition-duration, 300ms) ease-in-out;\n }\n\n .tab:hover .label {\n max-width: 200px;\n margin-left: 12px;\n opacity: 1;\n }\n `;\n }\n\n static TAB_HEIGHT = 56; // height of tab for auto-stacking\n static allTabs: FloatingTab[] = [];\n\n @property({ type: String })\n icon: string;\n\n @property({ type: String })\n label: string;\n\n @property({ type: String })\n color = '#6B7280';\n\n @property({ type: Number })\n top = -1; // -1 means auto-calculate position\n\n @property({ type: Boolean })\n hidden = false;\n\n connectedCallback() {\n super.connectedCallback();\n FloatingTab.allTabs.push(this);\n this.updatePosition();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n const index = FloatingTab.allTabs.indexOf(this);\n if (index > -1) {\n FloatingTab.allTabs.splice(index, 1);\n }\n // update positions of remaining tabs\n FloatingTab.allTabs.forEach((tab) => tab.updatePosition());\n }\n\n private updatePosition() {\n // if top is manually set, use it\n if (this.top !== -1) {\n return;\n }\n\n // auto-calculate position based on index\n const index = FloatingTab.allTabs.indexOf(this);\n if (index === -1) {\n this.top = 100; // default fallback\n } else {\n // start at 100px and stack with 10px gap between tabs\n this.top = 100 + index * (FloatingTab.TAB_HEIGHT + 10);\n }\n }\n\n public updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n if (changes.has('hidden')) {\n this.classList.toggle('hidden', this.hidden);\n }\n if (changes.has('top')) {\n this.updatePosition();\n }\n }\n\n private handleClick() {\n // hide all tabs when one is clicked\n FloatingTab.allTabs.forEach((tab) => {\n tab.hidden = true;\n });\n\n this.fireCustomEvent(CustomEventType.ButtonClicked, {\n action: 'toggle'\n });\n }\n\n public static showAllTabs() {\n FloatingTab.allTabs.forEach((tab) => {\n tab.hidden = false;\n });\n }\n\n public static hideAllTabs() {\n FloatingTab.allTabs.forEach((tab) => {\n tab.hidden = true;\n });\n }\n\n public render(): TemplateResult {\n const tabStyle = `\n background-color: ${this.color};\n top: ${this.top}px;\n `;\n\n const classes = getClasses({\n tab: true,\n hidden: this.hidden\n });\n\n return html`\n <div class=\"${classes}\" style=\"${tabStyle}\" @click=${this.handleClick}>\n <div class=\"icon-container\">\n ${this.icon\n ? html`<temba-icon size=\"2\" name=\"${this.icon}\"></temba-icon>`\n : ''}\n </div>\n <div class=\"label\">${this.label}</div>\n </div>\n `;\n }\n}\n"]}
@@ -11,13 +11,14 @@ export class ProgressBar extends RapidElement {
11
11
  this.done = false;
12
12
  this.showEstimatedCompletion = false;
13
13
  this.showPercentage = false;
14
+ this.animated = true;
14
15
  }
15
16
  updated(changes) {
16
17
  if (changes.has('eta') && this.eta) {
17
18
  this.estimatedCompletionDate = new Date(this.eta);
18
19
  this.showEstimatedCompletion = this.estimatedCompletionDate > new Date();
19
20
  }
20
- if (changes.has('current')) {
21
+ if (changes.has('current') || changes.has('total')) {
21
22
  const pct = Math.floor(Math.min((this.current / this.total) * 100, 100));
22
23
  if (Number.isNaN(pct)) {
23
24
  this.showPercentage = false;
@@ -30,8 +31,15 @@ export class ProgressBar extends RapidElement {
30
31
  }
31
32
  }
32
33
  render() {
34
+ const meterClasses = [
35
+ 'meter',
36
+ this.done ? 'done' : '',
37
+ this.animated ? '' : 'static'
38
+ ]
39
+ .filter(Boolean)
40
+ .join(' ');
33
41
  return html `<div class="wrapper ${this.done ? 'complete' : ''}">
34
- <div class="meter ${this.done ? 'done' : ''}">
42
+ <div class="${meterClasses}">
35
43
  ${this.message
36
44
  ? html `<div class="message">${this.message}</div>`
37
45
  : null}
@@ -132,6 +140,15 @@ ProgressBar.styles = css `
132
140
  display: none;
133
141
  }
134
142
 
143
+ .meter.static > span:after {
144
+ display: none;
145
+ animation: none;
146
+ }
147
+
148
+ .meter.static > span {
149
+ background-image: none;
150
+ }
151
+
135
152
  @keyframes move {
136
153
  0% {
137
154
  background-position: 0 0;
@@ -208,4 +225,7 @@ __decorate([
208
225
  __decorate([
209
226
  property({ type: String })
210
227
  ], ProgressBar.prototype, "message", void 0);
228
+ __decorate([
229
+ property({ type: Boolean })
230
+ ], ProgressBar.prototype, "animated", void 0);
211
231
  //# sourceMappingURL=ProgressBar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProgressBar.js","sourceRoot":"","sources":["../../../src/display/ProgressBar.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { RapidElement } from '../RapidElement';\nimport { property } from 'lit/decorators.js';\n\nexport class ProgressBar extends RapidElement {\n static styles = css`\n .wrapper {\n display: flex;\n box-sizing: content-box;\n background: #f1f1f1;\n border-radius: var(--curvature);\n box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.05);\n overflow: hidden;\n min-height: 1.5rem;\n }\n\n .message {\n padding: 0 0.5rem;\n color: rgba(0, 0, 0, 0.4);\n white-space: nowrap;\n }\n\n .meter {\n flex-grow: 1;\n display: flex;\n box-sizing: content-box;\n position: relative;\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n padding: 4px;\n min-height: 6px;\n }\n .meter > span {\n display: block;\n height: 100%;\n border-radius: calc(var(--curvature) * 0.8);\n background-color: var(--color-primary-dark);\n background-image: linear-gradient(\n center bottom,\n rgb(43, 194, 83) 37%,\n rgb(84, 240, 83) 69%\n );\n\n position: relative;\n overflow: hidden;\n }\n\n .meter > span:after,\n .animate > span > span {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n right: 0;\n background-image: linear-gradient(\n -45deg,\n rgba(255, 255, 255, 0.2) 25%,\n transparent 25%,\n transparent 50%,\n rgba(255, 255, 255, 0.2) 50%,\n rgba(255, 255, 255, 0.2) 75%,\n transparent 75%,\n transparent\n );\n z-index: 1;\n background-size: 50px 50px;\n animation: move 8s linear infinite;\n border-top-right-radius: var(--curvature);\n border-bottom-right-radius: var(--curvature);\n border-top-left-radius: var(--curvature);\n border-bottom-left-radius: var(--curvature);\n overflow: hidden;\n }\n\n .animate > span:after {\n display: none;\n }\n\n @keyframes move {\n 0% {\n background-position: 0 0;\n }\n 100% {\n background-position: 50px 50px;\n }\n }\n\n .meter .complete {\n transition: flex-basis 2s;\n }\n\n .meter .incomplete {\n flex-grow: 1;\n }\n\n .etc {\n display: flex;\n flex-direction: row;\n background: rgba(0, 0, 0, 0.07);\n font-weight: bold;\n white-space: nowrap;\n color: rgba(0, 0, 0, 0.5);\n align-self: center;\n padding: 0px 6px;\n align-self: stretch;\n align-items: center;\n }\n\n .etc > div {\n font-size: 0.7em;\n }\n\n .wrapper *::last-child {\n border-top-right-radius: var(--curvature);\n border-bottom-right-radius: var(--curvature);\n overflow: hidden;\n }\n\n .meter.done > span:after,\n .done .animate > span > span {\n display: none;\n }\n\n .meter.done > span {\n background: rgb(var(--success-rgb));\n }\n `;\n\n @property({ type: Number })\n total = 100;\n\n @property({ type: Number })\n current = 0;\n\n @property({ type: Number })\n pct = 0;\n\n @property({ type: Boolean })\n done = false;\n\n @property({ type: String })\n eta: string;\n\n @property({ type: String, attribute: false })\n estimatedCompletionDate: Date;\n\n @property({ type: Boolean })\n showEstimatedCompletion = false;\n\n @property({ type: Boolean })\n showPercentage = false;\n\n @property({ type: String })\n message: string;\n\n public updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n if (changes.has('eta') && this.eta) {\n this.estimatedCompletionDate = new Date(this.eta);\n this.showEstimatedCompletion = this.estimatedCompletionDate > new Date();\n }\n\n if (changes.has('current')) {\n const pct = Math.floor(Math.min((this.current / this.total) * 100, 100));\n if (Number.isNaN(pct)) {\n this.showPercentage = false;\n } else {\n this.pct = pct;\n this.showPercentage = true;\n }\n\n this.done = this.pct >= 100;\n }\n }\n\n public render(): TemplateResult {\n return html`<div class=\"wrapper ${this.done ? 'complete' : ''}\">\n <div class=\"meter ${this.done ? 'done' : ''}\">\n ${this.message\n ? html`<div class=\"message\">${this.message}</div>`\n : null}\n <span class=\"complete\" style=\"flex-basis: ${this.pct}%\"></span>\n <div class=\"incomplete\"></div>\n </div>\n\n ${this.showPercentage || this.showEstimatedCompletion\n ? html`<div class=\"etc\">\n <div>\n ${this.estimatedCompletionDate &&\n this.showEstimatedCompletion &&\n !this.done\n ? html`<temba-date\n value=\"${this.estimatedCompletionDate.toISOString()}\"\n display=\"countdown\"\n ></temba-date>`\n : html`${this.pct}%`}\n </div>\n </div>`\n : null}\n\n <slot></slot>\n </div>`;\n }\n}\n"]}
1
+ {"version":3,"file":"ProgressBar.js","sourceRoot":"","sources":["../../../src/display/ProgressBar.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { RapidElement } from '../RapidElement';\nimport { property } from 'lit/decorators.js';\n\nexport class ProgressBar extends RapidElement {\n static styles = css`\n .wrapper {\n display: flex;\n box-sizing: content-box;\n background: #f1f1f1;\n border-radius: var(--curvature);\n box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.05);\n overflow: hidden;\n min-height: 1.5rem;\n }\n\n .message {\n padding: 0 0.5rem;\n color: rgba(0, 0, 0, 0.4);\n white-space: nowrap;\n }\n\n .meter {\n flex-grow: 1;\n display: flex;\n box-sizing: content-box;\n position: relative;\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n padding: 4px;\n min-height: 6px;\n }\n .meter > span {\n display: block;\n height: 100%;\n border-radius: calc(var(--curvature) * 0.8);\n background-color: var(--color-primary-dark);\n background-image: linear-gradient(\n center bottom,\n rgb(43, 194, 83) 37%,\n rgb(84, 240, 83) 69%\n );\n\n position: relative;\n overflow: hidden;\n }\n\n .meter > span:after,\n .animate > span > span {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n right: 0;\n background-image: linear-gradient(\n -45deg,\n rgba(255, 255, 255, 0.2) 25%,\n transparent 25%,\n transparent 50%,\n rgba(255, 255, 255, 0.2) 50%,\n rgba(255, 255, 255, 0.2) 75%,\n transparent 75%,\n transparent\n );\n z-index: 1;\n background-size: 50px 50px;\n animation: move 8s linear infinite;\n border-top-right-radius: var(--curvature);\n border-bottom-right-radius: var(--curvature);\n border-top-left-radius: var(--curvature);\n border-bottom-left-radius: var(--curvature);\n overflow: hidden;\n }\n\n .animate > span:after {\n display: none;\n }\n\n .meter.static > span:after {\n display: none;\n animation: none;\n }\n\n .meter.static > span {\n background-image: none;\n }\n\n @keyframes move {\n 0% {\n background-position: 0 0;\n }\n 100% {\n background-position: 50px 50px;\n }\n }\n\n .meter .complete {\n transition: flex-basis 2s;\n }\n\n .meter .incomplete {\n flex-grow: 1;\n }\n\n .etc {\n display: flex;\n flex-direction: row;\n background: rgba(0, 0, 0, 0.07);\n font-weight: bold;\n white-space: nowrap;\n color: rgba(0, 0, 0, 0.5);\n align-self: center;\n padding: 0px 6px;\n align-self: stretch;\n align-items: center;\n }\n\n .etc > div {\n font-size: 0.7em;\n }\n\n .wrapper *::last-child {\n border-top-right-radius: var(--curvature);\n border-bottom-right-radius: var(--curvature);\n overflow: hidden;\n }\n\n .meter.done > span:after,\n .done .animate > span > span {\n display: none;\n }\n\n .meter.done > span {\n background: rgb(var(--success-rgb));\n }\n `;\n\n @property({ type: Number })\n total = 100;\n\n @property({ type: Number })\n current = 0;\n\n @property({ type: Number })\n pct = 0;\n\n @property({ type: Boolean })\n done = false;\n\n @property({ type: String })\n eta: string;\n\n @property({ type: String, attribute: false })\n estimatedCompletionDate: Date;\n\n @property({ type: Boolean })\n showEstimatedCompletion = false;\n\n @property({ type: Boolean })\n showPercentage = false;\n\n @property({ type: String })\n message: string;\n\n @property({ type: Boolean })\n animated = true;\n\n public updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n if (changes.has('eta') && this.eta) {\n this.estimatedCompletionDate = new Date(this.eta);\n this.showEstimatedCompletion = this.estimatedCompletionDate > new Date();\n }\n\n if (changes.has('current') || changes.has('total')) {\n const pct = Math.floor(Math.min((this.current / this.total) * 100, 100));\n if (Number.isNaN(pct)) {\n this.showPercentage = false;\n } else {\n this.pct = pct;\n this.showPercentage = true;\n }\n\n this.done = this.pct >= 100;\n }\n }\n\n public render(): TemplateResult {\n const meterClasses = [\n 'meter',\n this.done ? 'done' : '',\n this.animated ? '' : 'static'\n ]\n .filter(Boolean)\n .join(' ');\n\n return html`<div class=\"wrapper ${this.done ? 'complete' : ''}\">\n <div class=\"${meterClasses}\">\n ${this.message\n ? html`<div class=\"message\">${this.message}</div>`\n : null}\n <span class=\"complete\" style=\"flex-basis: ${this.pct}%\"></span>\n <div class=\"incomplete\"></div>\n </div>\n\n ${this.showPercentage || this.showEstimatedCompletion\n ? html`<div class=\"etc\">\n <div>\n ${this.estimatedCompletionDate &&\n this.showEstimatedCompletion &&\n !this.done\n ? html`<temba-date\n value=\"${this.estimatedCompletionDate.toISOString()}\"\n display=\"countdown\"\n ></temba-date>`\n : html`${this.pct}%`}\n </div>\n </div>`\n : null}\n\n <slot></slot>\n </div>`;\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/events.ts"],"names":[],"mappings":"","sourcesContent":["import { Msg, ObjectReference, User } from './interfaces';\n\nexport interface EventGroup {\n type: string;\n events: ContactEvent[];\n open: boolean;\n}\n\nexport interface ContactEvent {\n uuid?: string;\n type: string;\n created_on: string;\n _user?: ObjectReference;\n}\n\nexport interface ChannelEvent extends ContactEvent {\n channel_event_type: string;\n duration: number;\n\n event: {\n type: string;\n channel: { uuid: string; name: string };\n duration?: number;\n optin?: {\n uuid: string;\n name: string;\n };\n };\n}\n\nexport interface ContactLanguageChangedEvent extends ContactEvent {\n language: string;\n}\n\nexport interface ContactStatusChangedEvent extends ContactEvent {\n status: string;\n}\n\nexport interface OptInEvent extends ContactEvent {\n optin: {\n uuid: string;\n name: string;\n };\n}\n\nexport interface CallEvent extends ContactEvent {\n call?: {\n uuid: string;\n urn: string;\n };\n}\n\nexport interface ChatStartedEvent extends ContactEvent {\n params?: object;\n}\n\nexport interface MsgEvent extends ContactEvent {\n msg: Msg;\n optin?: ObjectReference;\n _status?: string | { status: string; changed_on: string; reason: string };\n _failed_reason?: string; // deprecated\n _logs_url?: string;\n}\n\nexport interface RunEvent extends ContactEvent {\n flow: ObjectReference;\n status: string;\n}\n\nexport interface URNsChangedEvent extends ContactEvent {\n urns: string[];\n}\n\nexport interface TicketEvent extends ContactEvent {\n ticket: {\n // ticket_opened\n uuid: string;\n topic?: ObjectReference;\n };\n ticket_uuid?: string; // all other event types\n assignee?: User;\n note?: string;\n topic?: ObjectReference;\n}\n\nexport interface NameChangedEvent extends ContactEvent {\n name: string;\n}\n\nexport interface UpdateFieldEvent extends ContactEvent {\n field: { key: string; name: string };\n value: { text: string };\n}\n\nexport interface ContactGroupsEvent extends ContactEvent {\n groups_added: ObjectReference[];\n groups_removed: ObjectReference[];\n}\n\nexport interface AirtimeTransferredEvent extends ContactEvent {\n sender: string;\n recipient: string;\n currency: string;\n amount: string;\n}\n\nexport type CallStartedEvent = ContactEvent;\n\nexport interface ContactHistoryPage {\n has_older: boolean;\n recent_only: boolean;\n next_before: number;\n next_after: number;\n start_date: Date;\n events: ContactEvent[];\n}\n"]}
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/events.ts"],"names":[],"mappings":"","sourcesContent":["import { Msg, ObjectReference, User } from './interfaces';\n\nexport interface EventGroup {\n type: string;\n events: ContactEvent[];\n open: boolean;\n}\n\nexport interface ContactEvent {\n uuid?: string;\n type: string;\n created_on: string;\n _user?: ObjectReference;\n}\n\nexport interface ChannelEvent extends ContactEvent {\n channel_event_type: string;\n duration: number;\n\n event: {\n type: string;\n channel: { uuid: string; name: string };\n duration?: number;\n optin?: {\n uuid: string;\n name: string;\n };\n };\n}\n\nexport interface ContactLanguageChangedEvent extends ContactEvent {\n language: string;\n}\n\nexport interface ContactStatusChangedEvent extends ContactEvent {\n status: string;\n}\n\nexport interface OptInEvent extends ContactEvent {\n optin: {\n uuid: string;\n name: string;\n };\n}\n\nexport interface CallEvent extends ContactEvent {\n call?: {\n uuid: string;\n urn: string;\n };\n}\n\nexport interface ChatStartedEvent extends ContactEvent {\n params?: object;\n}\n\nexport interface MsgEvent extends ContactEvent {\n msg: Msg;\n optin?: ObjectReference;\n _status?: { created_on: string; status: string; reason: string };\n _logs_url?: string;\n}\n\nexport interface RunEvent extends ContactEvent {\n flow: ObjectReference;\n status: string;\n}\n\nexport interface URNsChangedEvent extends ContactEvent {\n urns: string[];\n}\n\nexport interface TicketEvent extends ContactEvent {\n ticket: {\n // ticket_opened\n uuid: string;\n topic?: ObjectReference;\n };\n ticket_uuid?: string; // all other event types\n assignee?: User;\n note?: string;\n topic?: ObjectReference;\n}\n\nexport interface NameChangedEvent extends ContactEvent {\n name: string;\n}\n\nexport interface UpdateFieldEvent extends ContactEvent {\n field: { key: string; name: string };\n value: { text: string };\n}\n\nexport interface ContactGroupsEvent extends ContactEvent {\n groups_added: ObjectReference[];\n groups_removed: ObjectReference[];\n}\n\nexport interface AirtimeTransferredEvent extends ContactEvent {\n sender: string;\n recipient: string;\n currency: string;\n amount: string;\n}\n\nexport type CallStartedEvent = ContactEvent;\n\nexport interface ContactHistoryPage {\n events: ContactEvent[];\n has_older: boolean;\n recent_only: boolean;\n next_before: number;\n next_after: number;\n}\n"]}
@@ -8,6 +8,7 @@ import { RapidElement } from '../RapidElement';
8
8
  import { getClasses } from '../utils';
9
9
  import { getStore } from '../store/Store';
10
10
  import { CustomEventType } from '../interfaces';
11
+ import { fromStore, zustand } from '../store/AppState';
11
12
  const DRAG_THRESHOLD = 5;
12
13
  export class CanvasNode extends RapidElement {
13
14
  createRenderRoot() {
@@ -114,6 +115,20 @@ export class CanvasNode extends RapidElement {
114
115
 
115
116
  .node.execute-actions temba-sortable-list .action:last-child .body {
116
117
  padding-bottom: 1.5em;
118
+ }
119
+
120
+ /* Localization indicators */
121
+ .action.localizable:not(.has-localization) .action-content {
122
+ background: #fff8dc !important; /* Light yellow background for localizable but not yet localized */
123
+ }
124
+
125
+ .non-localizable {
126
+ opacity: 0.25;
127
+ pointer-events: none;
128
+ }
129
+
130
+ .action.non-localizable .action-content {
131
+ cursor: not-allowed;
117
132
  }
118
133
 
119
134
  .action .drag-handle {
@@ -182,6 +197,10 @@ export class CanvasNode extends RapidElement {
182
197
  margin: 0.2em;
183
198
  }
184
199
 
200
+ .router-section {
201
+ /* Container for router and categories */
202
+ }
203
+
185
204
  .categories {
186
205
  display: flex;
187
206
  flex-direction: row;
@@ -198,6 +217,11 @@ export class CanvasNode extends RapidElement {
198
217
  flex-direction: column;
199
218
  }
200
219
 
220
+ /* Localizable category - yellow background */
221
+ .category.localizable {
222
+ background-color: #fff8dc;
223
+ }
224
+
201
225
  .action-exits {
202
226
  padding-bottom: 0.7em;
203
227
  margin-top: -0.7em;
@@ -946,18 +970,20 @@ export class CanvasNode extends RapidElement {
946
970
  ? (_b = ACTION_GROUP_METADATA[config.group]) === null || _b === void 0 ? void 0 : _b.color
947
971
  : '#aaaaaa';
948
972
  return html `<div class="cn-title" style="background:${color}">
949
- ${((_d = (_c = this.node) === null || _c === void 0 ? void 0 : _c.actions) === null || _d === void 0 ? void 0 : _d.length) > 1
973
+ ${!this.isTranslating && ((_d = (_c = this.node) === null || _c === void 0 ? void 0 : _c.actions) === null || _d === void 0 ? void 0 : _d.length) > 1
950
974
  ? html `<temba-icon class="drag-handle" name="sort"></temba-icon>`
951
975
  : html `<div class="title-spacer"></div>`}
952
976
 
953
977
  <div class="name">${isRemoving ? 'Remove?' : config.name}</div>
954
- <div
955
- class="remove-button"
956
- @click=${(e) => this.handleActionRemoveClick(e, action, index)}
957
- title="Remove action"
958
- >
959
-
960
- </div>
978
+ ${!this.isTranslating
979
+ ? html `<div
980
+ class="remove-button"
981
+ @click=${(e) => this.handleActionRemoveClick(e, action, index)}
982
+ title="Remove action"
983
+ >
984
+
985
+ </div>`
986
+ : html `<div class="title-spacer"></div>`}
961
987
  </div>`;
962
988
  }
963
989
  renderNodeTitle(config, node, ui, isRemoving = false) {
@@ -979,13 +1005,15 @@ export class CanvasNode extends RapidElement {
979
1005
  ? config.renderTitle(node, ui)
980
1006
  : html `${config.name}`}
981
1007
  </div>
982
- <div
983
- class="remove-button"
984
- @click=${(e) => this.handleNodeRemoveClick(e)}
985
- title="Remove node"
986
- >
987
-
988
- </div>
1008
+ ${!this.isTranslating
1009
+ ? html `<div
1010
+ class="remove-button"
1011
+ @click=${(e) => this.handleNodeRemoveClick(e)}
1012
+ title="Remove node"
1013
+ >
1014
+
1015
+ </div>`
1016
+ : html `<div class="title-spacer"></div>`}
989
1017
  </div>`;
990
1018
  }
991
1019
  renderDropPlaceholder() {
@@ -996,24 +1024,80 @@ export class CanvasNode extends RapidElement {
996
1024
  style="height: ${height}px; background: #f3f4f6; border: 2px dashed #d1d5db; border-radius: var(--curvature);"
997
1025
  ></div>`;
998
1026
  }
1027
+ /**
1028
+ * Get the localized version of an action if translating, otherwise return the original action.
1029
+ * Falls back to base language values if no localization exists for a field.
1030
+ */
1031
+ getLocalizedAction(action) {
1032
+ var _b, _c, _d;
1033
+ // If not translating or no flow definition, return original action
1034
+ if (!this.isTranslating ||
1035
+ !this.flowDefinition ||
1036
+ !this.languageCode ||
1037
+ this.languageCode === this.flowDefinition.language) {
1038
+ return action;
1039
+ }
1040
+ // Check if there's localization for this action
1041
+ const localization = (_d = (_c = (_b = this.flowDefinition) === null || _b === void 0 ? void 0 : _b.localization) === null || _c === void 0 ? void 0 : _c[this.languageCode]) === null || _d === void 0 ? void 0 : _d[action.uuid];
1042
+ if (!localization) {
1043
+ // No localization available, return original action
1044
+ return action;
1045
+ }
1046
+ // Create a new action with localized values, falling back to base language
1047
+ const localizedAction = { ...action };
1048
+ // Apply localized values for each field
1049
+ Object.keys(localization).forEach((field) => {
1050
+ const localizedValue = localization[field];
1051
+ if (Array.isArray(localizedValue)) {
1052
+ // Localized values are stored as arrays
1053
+ if (localizedValue.length > 0) {
1054
+ // For single-value fields like 'text', take the first element
1055
+ // For array fields like 'quick_replies', use the whole array
1056
+ if (Array.isArray(action[field])) {
1057
+ localizedAction[field] = localizedValue;
1058
+ }
1059
+ else {
1060
+ localizedAction[field] = localizedValue[0];
1061
+ }
1062
+ }
1063
+ }
1064
+ });
1065
+ return localizedAction;
1066
+ }
999
1067
  renderAction(node, action, index) {
1068
+ var _b, _c, _d;
1000
1069
  const config = ACTION_CONFIG[action.type];
1001
1070
  const isRemoving = this.actionRemovingState.has(action.uuid);
1071
+ const isLocalizable = (config === null || config === void 0 ? void 0 : config.localizable) && config.localizable.length > 0;
1072
+ const isDisabled = this.isTranslating && !isLocalizable;
1073
+ // Check if this action has localization data
1074
+ const hasLocalization = this.isTranslating &&
1075
+ ((_d = (_c = (_b = this.flowDefinition) === null || _b === void 0 ? void 0 : _b.localization) === null || _c === void 0 ? void 0 : _c[this.languageCode]) === null || _d === void 0 ? void 0 : _d[action.uuid]);
1076
+ // Get the localized action if translating
1077
+ const displayAction = this.getLocalizedAction(action);
1002
1078
  if (config) {
1003
- return html `<div
1004
- class="action sortable ${action.type} ${isRemoving ? 'removing' : ''}"
1005
- id="action-${index}"
1006
- >
1079
+ const classes = [
1080
+ 'action',
1081
+ 'sortable',
1082
+ action.type,
1083
+ isRemoving ? 'removing' : '',
1084
+ isLocalizable && this.isTranslating ? 'localizable' : '',
1085
+ hasLocalization ? 'has-localization' : '',
1086
+ isDisabled ? 'non-localizable' : ''
1087
+ ]
1088
+ .filter(Boolean)
1089
+ .join(' ');
1090
+ return html `<div class="${classes}" id="action-${index}">
1007
1091
  <div
1008
1092
  class="action-content"
1009
- @mousedown=${(e) => this.handleActionMouseDown(e, action)}
1010
- @mouseup=${(e) => this.handleActionMouseUp(e, action)}
1011
- style="cursor: pointer; background: #fff"
1093
+ @mousedown=${(e) => !isDisabled && this.handleActionMouseDown(e, action)}
1094
+ @mouseup=${(e) => !isDisabled && this.handleActionMouseUp(e, action)}
1095
+ style="cursor: ${isDisabled ? 'not-allowed' : 'pointer'}"
1012
1096
  >
1013
1097
  ${this.renderTitle(config, action, index, isRemoving)}
1014
1098
  <div class="body">
1015
1099
  ${config.render
1016
- ? config.render(node, action)
1100
+ ? config.render(node, displayAction)
1017
1101
  : html `<pre>${action.type}</pre>`}
1018
1102
  </div>
1019
1103
  </div>
@@ -1071,19 +1155,48 @@ export class CanvasNode extends RapidElement {
1071
1155
  }
1072
1156
  }
1073
1157
  renderCategories(node) {
1158
+ var _b;
1074
1159
  if (!node.router || !node.router.categories) {
1075
1160
  return null;
1076
1161
  }
1162
+ // Check if this node type supports category localization
1163
+ const nodeConfig = NODE_CONFIG[(_b = this.ui) === null || _b === void 0 ? void 0 : _b.type];
1164
+ const supportsLocalization = (nodeConfig === null || nodeConfig === void 0 ? void 0 : nodeConfig.localizable) === 'categories';
1077
1165
  return html `<div class="categories">
1078
1166
  ${repeat(node.router.categories, (category) => category.uuid, (category) => {
1167
+ var _b, _c;
1079
1168
  const exit = node.exits.find((exit) => exit.uuid == category.exit_uuid);
1169
+ // Get localized category name if translating
1170
+ let displayName = category.name;
1171
+ let isLocalized = false;
1172
+ if (this.isTranslating &&
1173
+ this.languageCode !== 'eng' &&
1174
+ supportsLocalization) {
1175
+ const localization = (_c = (_b = this.flowDefinition) === null || _b === void 0 ? void 0 : _b.localization) === null || _c === void 0 ? void 0 : _c[this.languageCode];
1176
+ if (localization && localization[category.uuid]) {
1177
+ const categoryLocalization = localization[category.uuid];
1178
+ if (categoryLocalization.name && categoryLocalization.name[0]) {
1179
+ displayName = categoryLocalization.name[0];
1180
+ isLocalized = true;
1181
+ }
1182
+ }
1183
+ }
1184
+ // Category is localizable if: translating, supports localization, categories enabled, and not base language
1185
+ const isLocalizable = this.isTranslating &&
1186
+ this.languageCode !== 'eng' &&
1187
+ supportsLocalization &&
1188
+ this.includeCategoriesInTranslation &&
1189
+ !isLocalized;
1080
1190
  return html `<div
1081
- class="category"
1191
+ class=${getClasses({
1192
+ category: true,
1193
+ localizable: isLocalizable
1194
+ })}
1082
1195
  @mousedown=${(e) => this.handleNodeMouseDown(e)}
1083
1196
  @mouseup=${(e) => this.handleNodeMouseUp(e)}
1084
1197
  style="cursor: pointer;"
1085
1198
  >
1086
- <div class="cn-title">${category.name}</div>
1199
+ <div class="cn-title">${displayName}</div>
1087
1200
  ${this.renderExit(exit)}
1088
1201
  </div>`;
1089
1202
  })}
@@ -1107,12 +1220,19 @@ export class CanvasNode extends RapidElement {
1107
1220
  return html `<div class="node">Loading...</div>`;
1108
1221
  }
1109
1222
  const nodeConfig = NODE_CONFIG[this.ui.type];
1223
+ // Check if this node should be disabled (grayed out)
1224
+ const supportsLocalization = (nodeConfig === null || nodeConfig === void 0 ? void 0 : nodeConfig.localizable) === 'categories';
1225
+ const isNodeDisabled = this.isTranslating &&
1226
+ supportsLocalization &&
1227
+ !this.includeCategoriesInTranslation;
1110
1228
  return html `
1111
1229
  <div
1112
1230
  id="${this.node.uuid}"
1113
- class="node ${this.ui.type === 'execute_actions'
1114
- ? 'execute-actions'
1115
- : ''}"
1231
+ class=${getClasses({
1232
+ node: true,
1233
+ 'execute-actions': this.ui.type === 'execute_actions',
1234
+ 'non-localizable': isNodeDisabled
1235
+ })}
1116
1236
  style="left:${this.ui.position.left}px;top:${this.ui.position.top}px"
1117
1237
  >
1118
1238
  ${nodeConfig && nodeConfig.type !== 'execute_actions'
@@ -1144,12 +1264,14 @@ export class CanvasNode extends RapidElement {
1144
1264
  : html `${this.node.actions.map((action, index) => this.renderAction(this.node, action, index))}`
1145
1265
  : ''}
1146
1266
  ${this.node.router
1147
- ? html ` ${this.renderRouter(this.node.router, this.ui)}
1148
- ${this.renderCategories(this.node)}`
1267
+ ? html `<div class="router-section">
1268
+ ${this.renderRouter(this.node.router, this.ui)}
1269
+ ${this.renderCategories(this.node)}
1270
+ </div>`
1149
1271
  : html `<div class="action-exits">
1150
1272
  ${repeat(this.node.exits, (exit) => exit.uuid, (exit) => this.renderExit(exit))}
1151
1273
  </div>`}
1152
- ${this.ui.type === 'execute_actions'
1274
+ ${this.ui.type === 'execute_actions' && !this.isTranslating
1153
1275
  ? html `<div
1154
1276
  class="add-action-button"
1155
1277
  @click=${(e) => this.handleAddActionClick(e)}
@@ -1171,4 +1293,16 @@ __decorate([
1171
1293
  __decorate([
1172
1294
  property({ type: Object })
1173
1295
  ], CanvasNode.prototype, "ui", void 0);
1296
+ __decorate([
1297
+ fromStore(zustand, (state) => state.isTranslating)
1298
+ ], CanvasNode.prototype, "isTranslating", void 0);
1299
+ __decorate([
1300
+ fromStore(zustand, (state) => state.languageCode)
1301
+ ], CanvasNode.prototype, "languageCode", void 0);
1302
+ __decorate([
1303
+ fromStore(zustand, (state) => state.flowDefinition)
1304
+ ], CanvasNode.prototype, "flowDefinition", void 0);
1305
+ __decorate([
1306
+ fromStore(zustand, (state) => { var _b, _c, _d; return ((_d = (_c = (_b = state.flowDefinition) === null || _b === void 0 ? void 0 : _b._ui) === null || _c === void 0 ? void 0 : _c.translation_filters) === null || _d === void 0 ? void 0 : _d.categories) || false; })
1307
+ ], CanvasNode.prototype, "includeCategoriesInTranslation", void 0);
1174
1308
  //# sourceMappingURL=CanvasNode.js.map