@nyaruka/temba-components 0.141.1 → 0.142.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 (193) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/static/svg/index.svg +1 -1
  3. package/dist/temba-components.js +849 -655
  4. package/dist/temba-components.js.map +1 -1
  5. package/out-tsc/src/Icons.js +3 -1
  6. package/out-tsc/src/Icons.js.map +1 -1
  7. package/out-tsc/src/display/Button.js +2 -2
  8. package/out-tsc/src/display/Button.js.map +1 -1
  9. package/out-tsc/src/display/FloatingTab.js.map +1 -1
  10. package/out-tsc/src/flow/CanvasMenu.js +24 -1
  11. package/out-tsc/src/flow/CanvasMenu.js.map +1 -1
  12. package/out-tsc/src/flow/CanvasNode.js +7 -2
  13. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  14. package/out-tsc/src/flow/Editor.js +654 -66
  15. package/out-tsc/src/flow/Editor.js.map +1 -1
  16. package/out-tsc/src/flow/NodeEditor.js +8 -5
  17. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  18. package/out-tsc/src/flow/Plumber.js +40 -28
  19. package/out-tsc/src/flow/Plumber.js.map +1 -1
  20. package/out-tsc/src/flow/actions/send_msg.js +2 -1
  21. package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
  22. package/out-tsc/src/flow/nodes/wait_for_response.js +1 -1
  23. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
  24. package/out-tsc/src/flow/reflow.js +393 -0
  25. package/out-tsc/src/flow/reflow.js.map +1 -0
  26. package/out-tsc/src/flow/types.js.map +1 -1
  27. package/out-tsc/src/flow/utils.js +18 -3
  28. package/out-tsc/src/flow/utils.js.map +1 -1
  29. package/out-tsc/src/form/Compose.js +5 -0
  30. package/out-tsc/src/form/Compose.js.map +1 -1
  31. package/out-tsc/src/form/FieldRenderer.js +1 -3
  32. package/out-tsc/src/form/FieldRenderer.js.map +1 -1
  33. package/out-tsc/src/layout/Dialog.js +2 -0
  34. package/out-tsc/src/layout/Dialog.js.map +1 -1
  35. package/out-tsc/src/list/SortableList.js +39 -19
  36. package/out-tsc/src/list/SortableList.js.map +1 -1
  37. package/out-tsc/test/temba-canvas-menu.test.js +44 -0
  38. package/out-tsc/test/temba-canvas-menu.test.js.map +1 -1
  39. package/out-tsc/test/temba-flow-collision.test.js +25 -0
  40. package/out-tsc/test/temba-flow-collision.test.js.map +1 -1
  41. package/out-tsc/test/temba-flow-editor-zoom.test.js +491 -0
  42. package/out-tsc/test/temba-flow-editor-zoom.test.js.map +1 -0
  43. package/out-tsc/test/temba-flow-editor.test.js +145 -1
  44. package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
  45. package/out-tsc/test/temba-flow-node-drag.test.js +123 -0
  46. package/out-tsc/test/temba-flow-node-drag.test.js.map +1 -1
  47. package/out-tsc/test/temba-flow-plumber.test.js +31 -0
  48. package/out-tsc/test/temba-flow-plumber.test.js.map +1 -1
  49. package/out-tsc/test/temba-flow-reflow.test.js +472 -0
  50. package/out-tsc/test/temba-flow-reflow.test.js.map +1 -0
  51. package/out-tsc/test/temba-sortable-list.test.js +93 -0
  52. package/out-tsc/test/temba-sortable-list.test.js.map +1 -1
  53. package/package.json +1 -1
  54. package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
  55. package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
  56. package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
  57. package/screenshots/truth/actions/add_contact_urn/editor/expression-facebook.png +0 -0
  58. package/screenshots/truth/actions/add_contact_urn/editor/expression-phone.png +0 -0
  59. package/screenshots/truth/actions/add_contact_urn/editor/facebook-id.png +0 -0
  60. package/screenshots/truth/actions/add_contact_urn/editor/instagram-handle.png +0 -0
  61. package/screenshots/truth/actions/add_contact_urn/editor/line-id.png +0 -0
  62. package/screenshots/truth/actions/add_contact_urn/editor/phone-number.png +0 -0
  63. package/screenshots/truth/actions/add_contact_urn/editor/telegram-id.png +0 -0
  64. package/screenshots/truth/actions/add_contact_urn/editor/viber-id.png +0 -0
  65. package/screenshots/truth/actions/add_contact_urn/editor/wechat-id.png +0 -0
  66. package/screenshots/truth/actions/add_contact_urn/editor/whatsapp.png +0 -0
  67. package/screenshots/truth/actions/enter_flow/editor/basic-flow.png +0 -0
  68. package/screenshots/truth/actions/enter_flow/editor/long-flow-name.png +0 -0
  69. package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
  70. package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
  71. package/screenshots/truth/actions/say_msg/editor/multiline-text.png +0 -0
  72. package/screenshots/truth/actions/say_msg/editor/simple-text.png +0 -0
  73. package/screenshots/truth/actions/say_msg/editor/text-with-audio-url.png +0 -0
  74. package/screenshots/truth/actions/send_broadcast/editor/contacts-only.png +0 -0
  75. package/screenshots/truth/actions/send_broadcast/editor/groups-and-contacts.png +0 -0
  76. package/screenshots/truth/actions/send_broadcast/editor/groups-only.png +0 -0
  77. package/screenshots/truth/actions/send_broadcast/editor/many-groups.png +0 -0
  78. package/screenshots/truth/actions/send_broadcast/editor/multiline-text.png +0 -0
  79. package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
  80. package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
  81. package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
  82. package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
  83. package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
  84. package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
  85. package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
  86. package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
  87. package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
  88. package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
  89. package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
  90. package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
  91. package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
  92. package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
  93. package/screenshots/truth/actions/set_contact_channel/editor/sms-channel.png +0 -0
  94. package/screenshots/truth/actions/set_contact_channel/editor/whatsapp-channel.png +0 -0
  95. package/screenshots/truth/actions/set_contact_field/editor/clear-value.png +0 -0
  96. package/screenshots/truth/actions/set_contact_field/editor/set-value.png +0 -0
  97. package/screenshots/truth/actions/set_contact_language/editor/english.png +0 -0
  98. package/screenshots/truth/actions/set_contact_language/editor/french.png +0 -0
  99. package/screenshots/truth/actions/set_contact_status/editor/active.png +0 -0
  100. package/screenshots/truth/actions/set_contact_status/editor/archived.png +0 -0
  101. package/screenshots/truth/actions/set_contact_status/editor/blocked.png +0 -0
  102. package/screenshots/truth/actions/set_run_result/editor/expression-value.png +0 -0
  103. package/screenshots/truth/actions/set_run_result/editor/with-category.png +0 -0
  104. package/screenshots/truth/actions/start_session/editor/contact-query.png +0 -0
  105. package/screenshots/truth/actions/start_session/editor/contacts-only.png +0 -0
  106. package/screenshots/truth/actions/start_session/editor/create-contact.png +0 -0
  107. package/screenshots/truth/actions/start_session/editor/groups-and-contacts.png +0 -0
  108. package/screenshots/truth/actions/start_session/editor/groups-only.png +0 -0
  109. package/screenshots/truth/actions/start_session/editor/many-recipients.png +0 -0
  110. package/screenshots/truth/list/fields-dragging.png +0 -0
  111. package/screenshots/truth/list/sortable-dragging.png +0 -0
  112. package/screenshots/truth/modax/simple.png +0 -0
  113. package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
  114. package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
  115. package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
  116. package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
  117. package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
  118. package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
  119. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  120. package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
  121. package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
  122. package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
  123. package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
  124. package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
  125. package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
  126. package/screenshots/truth/nodes/wait_for_dial/editor/dial-with-limits.png +0 -0
  127. package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
  128. package/screenshots/truth/nodes/wait_for_digits/editor/digits-with-rules.png +0 -0
  129. package/screenshots/truth/nodes/wait_for_menu/editor/menu-with-digits.png +0 -0
  130. package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
  131. package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
  132. package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
  133. package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
  134. package/src/Icons.ts +3 -1
  135. package/src/display/Button.ts +2 -2
  136. package/src/display/FloatingTab.ts +1 -1
  137. package/src/flow/CanvasMenu.ts +28 -3
  138. package/src/flow/CanvasNode.ts +7 -2
  139. package/src/flow/Editor.ts +755 -75
  140. package/src/flow/NodeEditor.ts +8 -4
  141. package/src/flow/Plumber.ts +65 -35
  142. package/src/flow/actions/send_msg.ts +2 -1
  143. package/src/flow/nodes/wait_for_response.ts +1 -1
  144. package/src/flow/reflow.ts +534 -0
  145. package/src/flow/types.ts +1 -0
  146. package/src/flow/utils.ts +19 -3
  147. package/src/form/Compose.ts +5 -0
  148. package/src/form/FieldRenderer.ts +1 -3
  149. package/src/layout/Dialog.ts +2 -0
  150. package/src/list/SortableList.ts +40 -19
  151. package/static/svg/index.svg +1 -1
  152. package/static/svg/work/traced/expand-06.svg +1 -0
  153. package/static/svg/work/used/expand-06.svg +3 -0
  154. package/test/temba-canvas-menu.test.ts +55 -0
  155. package/test/temba-flow-collision.test.ts +31 -0
  156. package/test/temba-flow-editor-zoom.test.ts +583 -0
  157. package/test/temba-flow-editor.test.ts +187 -1
  158. package/test/temba-flow-node-drag.test.ts +171 -0
  159. package/test/temba-flow-plumber.test.ts +38 -0
  160. package/test/temba-flow-reflow.test.ts +703 -0
  161. package/test/temba-sortable-list.test.ts +120 -0
  162. package/screenshots/truth/actions/call_llm/editor/information-extraction.png +0 -0
  163. package/screenshots/truth/actions/call_llm/editor/sentiment-analysis.png +0 -0
  164. package/screenshots/truth/actions/call_llm/editor/summarization.png +0 -0
  165. package/screenshots/truth/actions/call_llm/editor/translation-task.png +0 -0
  166. package/screenshots/truth/actions/call_llm/render/information-extraction.png +0 -0
  167. package/screenshots/truth/actions/call_llm/render/sentiment-analysis.png +0 -0
  168. package/screenshots/truth/actions/call_llm/render/summarization.png +0 -0
  169. package/screenshots/truth/actions/call_llm/render/translation-task.png +0 -0
  170. package/screenshots/truth/actions/send_broadcast/editor/with-attachments.png +0 -0
  171. package/screenshots/truth/actions/send_broadcast/render/with-attachments.png +0 -0
  172. package/screenshots/truth/compose/attachments-with-failures.png +0 -0
  173. package/screenshots/truth/compose/attachments-with-files-and-failures.png +0 -0
  174. package/screenshots/truth/contacts/tickets-assignment.png +0 -0
  175. package/screenshots/truth/contacts/tickets.png +0 -0
  176. package/screenshots/truth/flow/editor-basic.png +0 -0
  177. package/screenshots/truth/formfield/markdown-errors.png +0 -0
  178. package/screenshots/truth/formfield/no-errors.png +0 -0
  179. package/screenshots/truth/formfield/plain-text-errors.png +0 -0
  180. package/screenshots/truth/formfield/widget-only-markdown-errors.png +0 -0
  181. package/screenshots/truth/omnibox/selected.png +0 -0
  182. package/screenshots/truth/select/enabled-multi-selection.png +0 -0
  183. package/screenshots/truth/select/endpoint-initial-value-updated.png +0 -0
  184. package/screenshots/truth/select/endpoint-initial-value.png +0 -0
  185. package/screenshots/truth/select/initial-value.png +0 -0
  186. package/screenshots/truth/select/multi-reorder-final.png +0 -0
  187. package/screenshots/truth/select/multi-reorder-initial.png +0 -0
  188. package/screenshots/truth/select/selected-multi-test.png +0 -0
  189. package/screenshots/truth/select/value-initial.png +0 -0
  190. package/screenshots/truth/wait-for-response/rules-editor.png +0 -0
  191. package/screenshots/truth/wait-for-response/timeout-editor-unchecked.png +0 -0
  192. package/screenshots/truth/wait-for-response/timeout-editor.png +0 -0
  193. package/screenshots/truth/webchat/connecting-state.png +0 -0
@@ -217,4 +217,124 @@ describe('temba-sortable-list', () => {
217
217
  const dragStopEvent = await dragStop;
218
218
  expect(dragStopEvent.detail.isExternal).to.be.true;
219
219
  });
220
+
221
+ describe('zoom-aware dimensions', () => {
222
+ it('stores layout dimensions on mousedown via offsetWidth/offsetHeight', async () => {
223
+ const list: SortableList = await createSorter(BORING_LIST);
224
+ await list.updateComplete;
225
+
226
+ const bounds = list.getBoundingClientRect();
227
+
228
+ // Start drag
229
+ await moveMouse(bounds.left + 20, bounds.bottom - 10);
230
+ await mouseDown();
231
+
232
+ // originalLayoutSize should be set using offsetWidth/offsetHeight
233
+ expect(list.originalLayoutSize).to.not.be.null;
234
+ // At zoom=1.0, layout size equals viewport size
235
+ expect(list.originalLayoutSize.width).to.equal(
236
+ list.originalElementRect.width
237
+ );
238
+ expect(list.originalLayoutSize.height).to.equal(
239
+ list.originalElementRect.height
240
+ );
241
+
242
+ await mouseUp();
243
+ clock.runAll();
244
+ });
245
+
246
+ it('ghost uses scale(1.03) without ancestor transform', async () => {
247
+ const list: SortableList = await createSorter(BORING_LIST);
248
+ await list.updateComplete;
249
+
250
+ const bounds = list.getBoundingClientRect();
251
+
252
+ // Start drag past threshold to create ghost
253
+ await moveMouse(bounds.left + 20, bounds.bottom - 10);
254
+ await mouseDown();
255
+ await moveMouse(bounds.left + 30, bounds.bottom - 10);
256
+ clock.runAll();
257
+
258
+ expect(list.ghostElement).to.not.be.null;
259
+ expect(list.ghostElement.style.transform).to.equal('scale(1.03)');
260
+ // transformOrigin should NOT be set to '0 0' when there's no ancestor scale
261
+ expect(list.ghostElement.style.transformOrigin).to.not.equal('0 0');
262
+
263
+ await mouseUp();
264
+ clock.runAll();
265
+ });
266
+
267
+ it('detects ancestor scale and applies it to ghost', () => {
268
+ // Unit test the ghost scaling logic directly:
269
+ // When originalElementRect (viewport) differs from originalLayoutSize (layout),
270
+ // the ancestor scale is detected and applied to the ghost.
271
+ const list = new SortableList();
272
+
273
+ // Simulate being inside a container with transform: scale(0.5)
274
+ // Layout dimensions: 100x20, viewport dimensions: 50x10
275
+ list.originalElementRect = {
276
+ width: 50,
277
+ height: 10,
278
+ left: 0,
279
+ top: 0,
280
+ right: 50,
281
+ bottom: 10,
282
+ x: 0,
283
+ y: 0,
284
+ toJSON: () => ({})
285
+ } as DOMRect;
286
+ list.originalLayoutSize = { width: 100, height: 20 };
287
+
288
+ // Calculate ancestor scale the same way the component does
289
+ const ancestorScale =
290
+ list.originalLayoutSize.width > 0
291
+ ? list.originalElementRect.width / list.originalLayoutSize.width
292
+ : 1;
293
+ const hasAncestorScale = Math.abs(ancestorScale - 1) > 0.001;
294
+
295
+ expect(hasAncestorScale).to.be.true;
296
+ expect(ancestorScale).to.equal(0.5);
297
+
298
+ // The ghost would get: transform: scale(0.5 * 1.03) = scale(0.515)
299
+ const expectedScale = ancestorScale * 1.03;
300
+ expect(expectedScale).to.be.closeTo(0.515, 0.001);
301
+ });
302
+
303
+ it('uses originalLayoutSize for placeholder sizing', () => {
304
+ // Verify that the component stores separate layout vs viewport dimensions
305
+ const list = new SortableList();
306
+
307
+ // At zoom=1.0, both should be the same
308
+ const mockRect = {
309
+ width: 100,
310
+ height: 20,
311
+ left: 0,
312
+ top: 0,
313
+ right: 100,
314
+ bottom: 20,
315
+ x: 0,
316
+ y: 0,
317
+ toJSON: () => ({})
318
+ } as DOMRect;
319
+
320
+ list.originalElementRect = mockRect;
321
+ list.originalLayoutSize = { width: 100, height: 20 };
322
+
323
+ // At zoom=1.0, they match
324
+ expect(list.originalLayoutSize.width).to.equal(
325
+ list.originalElementRect.width
326
+ );
327
+
328
+ // At zoom=0.5, viewport rect would be half but layout stays the same
329
+ list.originalElementRect = {
330
+ ...mockRect,
331
+ width: 50,
332
+ height: 10
333
+ } as DOMRect;
334
+
335
+ // Layout size is independent of zoom
336
+ expect(list.originalLayoutSize.width).to.equal(100);
337
+ expect(list.originalLayoutSize.height).to.equal(20);
338
+ });
339
+ });
220
340
  });