@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.
- package/CHANGELOG.md +15 -0
- package/dist/static/svg/index.svg +1 -1
- package/dist/temba-components.js +849 -655
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/Icons.js +3 -1
- package/out-tsc/src/Icons.js.map +1 -1
- package/out-tsc/src/display/Button.js +2 -2
- package/out-tsc/src/display/Button.js.map +1 -1
- package/out-tsc/src/display/FloatingTab.js.map +1 -1
- package/out-tsc/src/flow/CanvasMenu.js +24 -1
- package/out-tsc/src/flow/CanvasMenu.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +7 -2
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +654 -66
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +8 -5
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/Plumber.js +40 -28
- package/out-tsc/src/flow/Plumber.js.map +1 -1
- package/out-tsc/src/flow/actions/send_msg.js +2 -1
- package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/flow/reflow.js +393 -0
- package/out-tsc/src/flow/reflow.js.map +1 -0
- package/out-tsc/src/flow/types.js.map +1 -1
- package/out-tsc/src/flow/utils.js +18 -3
- package/out-tsc/src/flow/utils.js.map +1 -1
- package/out-tsc/src/form/Compose.js +5 -0
- package/out-tsc/src/form/Compose.js.map +1 -1
- package/out-tsc/src/form/FieldRenderer.js +1 -3
- package/out-tsc/src/form/FieldRenderer.js.map +1 -1
- package/out-tsc/src/layout/Dialog.js +2 -0
- package/out-tsc/src/layout/Dialog.js.map +1 -1
- package/out-tsc/src/list/SortableList.js +39 -19
- package/out-tsc/src/list/SortableList.js.map +1 -1
- package/out-tsc/test/temba-canvas-menu.test.js +44 -0
- package/out-tsc/test/temba-canvas-menu.test.js.map +1 -1
- package/out-tsc/test/temba-flow-collision.test.js +25 -0
- package/out-tsc/test/temba-flow-collision.test.js.map +1 -1
- package/out-tsc/test/temba-flow-editor-zoom.test.js +491 -0
- package/out-tsc/test/temba-flow-editor-zoom.test.js.map +1 -0
- package/out-tsc/test/temba-flow-editor.test.js +145 -1
- package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
- package/out-tsc/test/temba-flow-node-drag.test.js +123 -0
- package/out-tsc/test/temba-flow-node-drag.test.js.map +1 -1
- package/out-tsc/test/temba-flow-plumber.test.js +31 -0
- package/out-tsc/test/temba-flow-plumber.test.js.map +1 -1
- package/out-tsc/test/temba-flow-reflow.test.js +472 -0
- package/out-tsc/test/temba-flow-reflow.test.js.map +1 -0
- package/out-tsc/test/temba-sortable-list.test.js +93 -0
- package/out-tsc/test/temba-sortable-list.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/expression-facebook.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/expression-phone.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/facebook-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/instagram-handle.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/line-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/phone-number.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/telegram-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/viber-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/wechat-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/whatsapp.png +0 -0
- package/screenshots/truth/actions/enter_flow/editor/basic-flow.png +0 -0
- package/screenshots/truth/actions/enter_flow/editor/long-flow-name.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/say_msg/editor/multiline-text.png +0 -0
- package/screenshots/truth/actions/say_msg/editor/simple-text.png +0 -0
- package/screenshots/truth/actions/say_msg/editor/text-with-audio-url.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/contacts-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/groups-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/multiline-text.png +0 -0
- package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
- package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
- package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
- package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
- package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
- package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
- package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
- package/screenshots/truth/actions/set_contact_channel/editor/sms-channel.png +0 -0
- package/screenshots/truth/actions/set_contact_channel/editor/whatsapp-channel.png +0 -0
- package/screenshots/truth/actions/set_contact_field/editor/clear-value.png +0 -0
- package/screenshots/truth/actions/set_contact_field/editor/set-value.png +0 -0
- package/screenshots/truth/actions/set_contact_language/editor/english.png +0 -0
- package/screenshots/truth/actions/set_contact_language/editor/french.png +0 -0
- package/screenshots/truth/actions/set_contact_status/editor/active.png +0 -0
- package/screenshots/truth/actions/set_contact_status/editor/archived.png +0 -0
- package/screenshots/truth/actions/set_contact_status/editor/blocked.png +0 -0
- package/screenshots/truth/actions/set_run_result/editor/expression-value.png +0 -0
- package/screenshots/truth/actions/set_run_result/editor/with-category.png +0 -0
- package/screenshots/truth/actions/start_session/editor/contact-query.png +0 -0
- package/screenshots/truth/actions/start_session/editor/contacts-only.png +0 -0
- package/screenshots/truth/actions/start_session/editor/create-contact.png +0 -0
- package/screenshots/truth/actions/start_session/editor/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/start_session/editor/groups-only.png +0 -0
- package/screenshots/truth/actions/start_session/editor/many-recipients.png +0 -0
- package/screenshots/truth/list/fields-dragging.png +0 -0
- package/screenshots/truth/list/sortable-dragging.png +0 -0
- package/screenshots/truth/modax/simple.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
- package/screenshots/truth/nodes/wait_for_dial/editor/dial-with-limits.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/digits-with-rules.png +0 -0
- package/screenshots/truth/nodes/wait_for_menu/editor/menu-with-digits.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
- package/src/Icons.ts +3 -1
- package/src/display/Button.ts +2 -2
- package/src/display/FloatingTab.ts +1 -1
- package/src/flow/CanvasMenu.ts +28 -3
- package/src/flow/CanvasNode.ts +7 -2
- package/src/flow/Editor.ts +755 -75
- package/src/flow/NodeEditor.ts +8 -4
- package/src/flow/Plumber.ts +65 -35
- package/src/flow/actions/send_msg.ts +2 -1
- package/src/flow/nodes/wait_for_response.ts +1 -1
- package/src/flow/reflow.ts +534 -0
- package/src/flow/types.ts +1 -0
- package/src/flow/utils.ts +19 -3
- package/src/form/Compose.ts +5 -0
- package/src/form/FieldRenderer.ts +1 -3
- package/src/layout/Dialog.ts +2 -0
- package/src/list/SortableList.ts +40 -19
- package/static/svg/index.svg +1 -1
- package/static/svg/work/traced/expand-06.svg +1 -0
- package/static/svg/work/used/expand-06.svg +3 -0
- package/test/temba-canvas-menu.test.ts +55 -0
- package/test/temba-flow-collision.test.ts +31 -0
- package/test/temba-flow-editor-zoom.test.ts +583 -0
- package/test/temba-flow-editor.test.ts +187 -1
- package/test/temba-flow-node-drag.test.ts +171 -0
- package/test/temba-flow-plumber.test.ts +38 -0
- package/test/temba-flow-reflow.test.ts +703 -0
- package/test/temba-sortable-list.test.ts +120 -0
- package/screenshots/truth/actions/call_llm/editor/information-extraction.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/sentiment-analysis.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/summarization.png +0 -0
- package/screenshots/truth/actions/call_llm/editor/translation-task.png +0 -0
- package/screenshots/truth/actions/call_llm/render/information-extraction.png +0 -0
- package/screenshots/truth/actions/call_llm/render/sentiment-analysis.png +0 -0
- package/screenshots/truth/actions/call_llm/render/summarization.png +0 -0
- package/screenshots/truth/actions/call_llm/render/translation-task.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/with-attachments.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/with-attachments.png +0 -0
- package/screenshots/truth/compose/attachments-with-failures.png +0 -0
- package/screenshots/truth/compose/attachments-with-files-and-failures.png +0 -0
- package/screenshots/truth/contacts/tickets-assignment.png +0 -0
- package/screenshots/truth/contacts/tickets.png +0 -0
- package/screenshots/truth/flow/editor-basic.png +0 -0
- package/screenshots/truth/formfield/markdown-errors.png +0 -0
- package/screenshots/truth/formfield/no-errors.png +0 -0
- package/screenshots/truth/formfield/plain-text-errors.png +0 -0
- package/screenshots/truth/formfield/widget-only-markdown-errors.png +0 -0
- package/screenshots/truth/omnibox/selected.png +0 -0
- package/screenshots/truth/select/enabled-multi-selection.png +0 -0
- package/screenshots/truth/select/endpoint-initial-value-updated.png +0 -0
- package/screenshots/truth/select/endpoint-initial-value.png +0 -0
- package/screenshots/truth/select/initial-value.png +0 -0
- package/screenshots/truth/select/multi-reorder-final.png +0 -0
- package/screenshots/truth/select/multi-reorder-initial.png +0 -0
- package/screenshots/truth/select/selected-multi-test.png +0 -0
- package/screenshots/truth/select/value-initial.png +0 -0
- package/screenshots/truth/wait-for-response/rules-editor.png +0 -0
- package/screenshots/truth/wait-for-response/timeout-editor-unchecked.png +0 -0
- package/screenshots/truth/wait-for-response/timeout-editor.png +0 -0
- package/screenshots/truth/webchat/connecting-state.png +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { expect, fixture, oneEvent } from '@open-wc/testing';
|
|
2
2
|
import { html } from 'lit';
|
|
3
3
|
import { CustomEventType } from '../src/interfaces';
|
|
4
|
+
import { SortableList } from '../src/list/SortableList';
|
|
4
5
|
import { assertScreenshot, getClip } from './utils.test';
|
|
5
6
|
import { useFakeTimers } from 'sinon';
|
|
6
7
|
const BORING_LIST = html `
|
|
@@ -171,5 +172,97 @@ describe('temba-sortable-list', () => {
|
|
|
171
172
|
const dragStopEvent = await dragStop;
|
|
172
173
|
expect(dragStopEvent.detail.isExternal).to.be.true;
|
|
173
174
|
});
|
|
175
|
+
describe('zoom-aware dimensions', () => {
|
|
176
|
+
it('stores layout dimensions on mousedown via offsetWidth/offsetHeight', async () => {
|
|
177
|
+
const list = await createSorter(BORING_LIST);
|
|
178
|
+
await list.updateComplete;
|
|
179
|
+
const bounds = list.getBoundingClientRect();
|
|
180
|
+
// Start drag
|
|
181
|
+
await moveMouse(bounds.left + 20, bounds.bottom - 10);
|
|
182
|
+
await mouseDown();
|
|
183
|
+
// originalLayoutSize should be set using offsetWidth/offsetHeight
|
|
184
|
+
expect(list.originalLayoutSize).to.not.be.null;
|
|
185
|
+
// At zoom=1.0, layout size equals viewport size
|
|
186
|
+
expect(list.originalLayoutSize.width).to.equal(list.originalElementRect.width);
|
|
187
|
+
expect(list.originalLayoutSize.height).to.equal(list.originalElementRect.height);
|
|
188
|
+
await mouseUp();
|
|
189
|
+
clock.runAll();
|
|
190
|
+
});
|
|
191
|
+
it('ghost uses scale(1.03) without ancestor transform', async () => {
|
|
192
|
+
const list = await createSorter(BORING_LIST);
|
|
193
|
+
await list.updateComplete;
|
|
194
|
+
const bounds = list.getBoundingClientRect();
|
|
195
|
+
// Start drag past threshold to create ghost
|
|
196
|
+
await moveMouse(bounds.left + 20, bounds.bottom - 10);
|
|
197
|
+
await mouseDown();
|
|
198
|
+
await moveMouse(bounds.left + 30, bounds.bottom - 10);
|
|
199
|
+
clock.runAll();
|
|
200
|
+
expect(list.ghostElement).to.not.be.null;
|
|
201
|
+
expect(list.ghostElement.style.transform).to.equal('scale(1.03)');
|
|
202
|
+
// transformOrigin should NOT be set to '0 0' when there's no ancestor scale
|
|
203
|
+
expect(list.ghostElement.style.transformOrigin).to.not.equal('0 0');
|
|
204
|
+
await mouseUp();
|
|
205
|
+
clock.runAll();
|
|
206
|
+
});
|
|
207
|
+
it('detects ancestor scale and applies it to ghost', () => {
|
|
208
|
+
// Unit test the ghost scaling logic directly:
|
|
209
|
+
// When originalElementRect (viewport) differs from originalLayoutSize (layout),
|
|
210
|
+
// the ancestor scale is detected and applied to the ghost.
|
|
211
|
+
const list = new SortableList();
|
|
212
|
+
// Simulate being inside a container with transform: scale(0.5)
|
|
213
|
+
// Layout dimensions: 100x20, viewport dimensions: 50x10
|
|
214
|
+
list.originalElementRect = {
|
|
215
|
+
width: 50,
|
|
216
|
+
height: 10,
|
|
217
|
+
left: 0,
|
|
218
|
+
top: 0,
|
|
219
|
+
right: 50,
|
|
220
|
+
bottom: 10,
|
|
221
|
+
x: 0,
|
|
222
|
+
y: 0,
|
|
223
|
+
toJSON: () => ({})
|
|
224
|
+
};
|
|
225
|
+
list.originalLayoutSize = { width: 100, height: 20 };
|
|
226
|
+
// Calculate ancestor scale the same way the component does
|
|
227
|
+
const ancestorScale = list.originalLayoutSize.width > 0
|
|
228
|
+
? list.originalElementRect.width / list.originalLayoutSize.width
|
|
229
|
+
: 1;
|
|
230
|
+
const hasAncestorScale = Math.abs(ancestorScale - 1) > 0.001;
|
|
231
|
+
expect(hasAncestorScale).to.be.true;
|
|
232
|
+
expect(ancestorScale).to.equal(0.5);
|
|
233
|
+
// The ghost would get: transform: scale(0.5 * 1.03) = scale(0.515)
|
|
234
|
+
const expectedScale = ancestorScale * 1.03;
|
|
235
|
+
expect(expectedScale).to.be.closeTo(0.515, 0.001);
|
|
236
|
+
});
|
|
237
|
+
it('uses originalLayoutSize for placeholder sizing', () => {
|
|
238
|
+
// Verify that the component stores separate layout vs viewport dimensions
|
|
239
|
+
const list = new SortableList();
|
|
240
|
+
// At zoom=1.0, both should be the same
|
|
241
|
+
const mockRect = {
|
|
242
|
+
width: 100,
|
|
243
|
+
height: 20,
|
|
244
|
+
left: 0,
|
|
245
|
+
top: 0,
|
|
246
|
+
right: 100,
|
|
247
|
+
bottom: 20,
|
|
248
|
+
x: 0,
|
|
249
|
+
y: 0,
|
|
250
|
+
toJSON: () => ({})
|
|
251
|
+
};
|
|
252
|
+
list.originalElementRect = mockRect;
|
|
253
|
+
list.originalLayoutSize = { width: 100, height: 20 };
|
|
254
|
+
// At zoom=1.0, they match
|
|
255
|
+
expect(list.originalLayoutSize.width).to.equal(list.originalElementRect.width);
|
|
256
|
+
// At zoom=0.5, viewport rect would be half but layout stays the same
|
|
257
|
+
list.originalElementRect = {
|
|
258
|
+
...mockRect,
|
|
259
|
+
width: 50,
|
|
260
|
+
height: 10
|
|
261
|
+
};
|
|
262
|
+
// Layout size is independent of zoom
|
|
263
|
+
expect(list.originalLayoutSize.width).to.equal(100);
|
|
264
|
+
expect(list.originalLayoutSize.height).to.equal(20);
|
|
265
|
+
});
|
|
266
|
+
});
|
|
174
267
|
});
|
|
175
268
|
//# sourceMappingURL=temba-sortable-list.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temba-sortable-list.test.js","sourceRoot":"","sources":["../../test/temba-sortable-list.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, fixture, oneEvent } from '@open-wc/testing';\nimport { html, TemplateResult } from 'lit';\nimport { CustomEventType } from '../src/interfaces';\nimport { SortableList } from '../src/list/SortableList';\nimport { assertScreenshot, getClip } from './utils.test';\nimport Sinon, { useFakeTimers } from 'sinon';\n\nconst BORING_LIST = html`\n <temba-sortable-list>\n <style>\n .sortable {\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n height: 20px;\n }\n </style>\n <div class=\"sortable\" id=\"chicken\" style=\"\">Chicken</div>\n <div class=\"sortable\" id=\"fish\">Fish</div>\n </temba-sortable-list>\n`;\n\nconst HORIZONTAL_LIST = html`\n <temba-sortable-list horizontal>\n <style>\n .sortable {\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n height: 20px;\n width: 50px;\n }\n </style>\n <div class=\"sortable\" id=\"red\">Red</div>\n <div class=\"sortable\" id=\"blue\">Blue</div>\n <div class=\"sortable\" id=\"green\">Green</div>\n </temba-sortable-list>\n`;\n\nconst createSorter = async (def: TemplateResult) => {\n const parentNode = document.createElement('div');\n parentNode.setAttribute('style', 'width: 100px;');\n return (await fixture(def, { parentNode })) as SortableList;\n};\n\ndescribe('temba-sortable-list', () => {\n let clock: Sinon.SinonFakeTimers;\n beforeEach(function () {\n clock = useFakeTimers();\n clock.runAll();\n });\n\n afterEach(function () {\n clock.restore();\n });\n\n it('renders default', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n await assertScreenshot('list/sortable', getClip(list));\n });\n\n it('can get ids of sortable elements', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n await list.updateComplete;\n\n const ids = list.getIds();\n expect(ids).to.deep.equal(['chicken', 'fish']);\n });\n\n it('works with horizontal layout', async () => {\n const list: SortableList = await createSorter(HORIZONTAL_LIST);\n await list.updateComplete;\n\n const ids = list.getIds();\n expect(ids).to.deep.equal(['red', 'blue', 'green']);\n\n // Test horizontal drag behavior\n const bounds = list.getBoundingClientRect();\n const orderChanged = oneEvent(list, CustomEventType.OrderChanged, false);\n\n // Drag the first item (red) to after the second item (blue)\n await moveMouse(bounds.left + 10, bounds.top + 10);\n await mouseDown();\n await moveMouse(bounds.left + 80, bounds.top + 10);\n await mouseUp();\n clock.runAll();\n\n const orderEvent = await orderChanged;\n expect(orderEvent.detail).to.deep.equal({\n swap: [0, 2]\n });\n });\n\n it('handles prepareGhost callback', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n let ghostPrepared = false;\n\n list.prepareGhost = (ghost: HTMLElement) => {\n ghostPrepared = true;\n ghost.style.backgroundColor = 'red';\n };\n\n const bounds = list.getBoundingClientRect();\n\n // Start dragging to trigger ghost creation\n await moveMouse(bounds.left + 20, bounds.bottom - 10);\n await mouseDown();\n await moveMouse(bounds.left + 30, bounds.bottom - 10);\n\n expect(ghostPrepared).to.be.true;\n\n // Clean up\n await mouseUp();\n clock.runAll();\n });\n\n it('drags', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n const updated = oneEvent(list, 'change', false);\n\n const bounds = list.getBoundingClientRect();\n\n await moveMouse(bounds.left + 20, bounds.bottom - 10);\n await mouseDown();\n await moveMouse(bounds.left + 20, bounds.top + 5);\n\n // should be hovered\n await assertScreenshot('list/sortable-dragging', getClip(list));\n\n // now lets drop - this will fire the order changed event\n const orderChanged = oneEvent(list, CustomEventType.OrderChanged, false);\n await mouseUp();\n clock.runAll();\n await list.updateComplete;\n clock.runAll();\n\n // we should fire an order changed event on drop\n const orderEvent = await orderChanged;\n expect(orderEvent.detail).to.deep.equal({\n swap: [1, 0]\n });\n\n await assertScreenshot('list/sortable-dropped', getClip(list));\n\n // we should fire a change event\n const changeEvent = await updated;\n expect(changeEvent.type).to.equal('change');\n });\n\n it('detects external drag when dragging outside container', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n list.externalDrag = true;\n await list.updateComplete;\n\n const bounds = list.getBoundingClientRect();\n\n // track external drag events\n let externalDragFired = false;\n let internalDragFired = false;\n\n list.addEventListener(CustomEventType.DragExternal, () => {\n externalDragFired = true;\n });\n\n list.addEventListener(CustomEventType.DragInternal, () => {\n internalDragFired = true;\n });\n\n // start dragging an item\n await moveMouse(bounds.left + 20, bounds.bottom - 10);\n await mouseDown();\n\n // drag outside the container (far to the right)\n await moveMouse(bounds.right + 100, bounds.top + 10);\n clock.runAll();\n\n // should have fired external drag event\n expect(externalDragFired).to.be.true;\n\n // drag back inside\n externalDragFired = false; // reset\n await moveMouse(bounds.left + 20, bounds.top + 10);\n clock.runAll();\n\n // should have fired internal drag event\n expect(internalDragFired).to.be.true;\n\n // clean up\n await mouseUp();\n clock.runAll();\n });\n\n it('fires DragStop with isExternal=true when dropped outside container', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n list.externalDrag = true;\n await list.updateComplete;\n\n const bounds = list.getBoundingClientRect();\n\n // start dragging an item\n await moveMouse(bounds.left + 20, bounds.bottom - 10);\n await mouseDown();\n\n // drag outside the container\n await moveMouse(bounds.right + 100, bounds.top + 10);\n clock.runAll();\n\n // listen for drag stop event\n const dragStop = oneEvent(list, CustomEventType.DragStop, false);\n\n // drop outside\n await mouseUp();\n clock.runAll();\n\n const dragStopEvent = await dragStop;\n expect(dragStopEvent.detail.isExternal).to.be.true;\n });\n});\n"]}
|
|
1
|
+
{"version":3,"file":"temba-sortable-list.test.js","sourceRoot":"","sources":["../../test/temba-sortable-list.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, fixture, oneEvent } from '@open-wc/testing';\nimport { html, TemplateResult } from 'lit';\nimport { CustomEventType } from '../src/interfaces';\nimport { SortableList } from '../src/list/SortableList';\nimport { assertScreenshot, getClip } from './utils.test';\nimport Sinon, { useFakeTimers } from 'sinon';\n\nconst BORING_LIST = html`\n <temba-sortable-list>\n <style>\n .sortable {\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n height: 20px;\n }\n </style>\n <div class=\"sortable\" id=\"chicken\" style=\"\">Chicken</div>\n <div class=\"sortable\" id=\"fish\">Fish</div>\n </temba-sortable-list>\n`;\n\nconst HORIZONTAL_LIST = html`\n <temba-sortable-list horizontal>\n <style>\n .sortable {\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n height: 20px;\n width: 50px;\n }\n </style>\n <div class=\"sortable\" id=\"red\">Red</div>\n <div class=\"sortable\" id=\"blue\">Blue</div>\n <div class=\"sortable\" id=\"green\">Green</div>\n </temba-sortable-list>\n`;\n\nconst createSorter = async (def: TemplateResult) => {\n const parentNode = document.createElement('div');\n parentNode.setAttribute('style', 'width: 100px;');\n return (await fixture(def, { parentNode })) as SortableList;\n};\n\ndescribe('temba-sortable-list', () => {\n let clock: Sinon.SinonFakeTimers;\n beforeEach(function () {\n clock = useFakeTimers();\n clock.runAll();\n });\n\n afterEach(function () {\n clock.restore();\n });\n\n it('renders default', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n await assertScreenshot('list/sortable', getClip(list));\n });\n\n it('can get ids of sortable elements', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n await list.updateComplete;\n\n const ids = list.getIds();\n expect(ids).to.deep.equal(['chicken', 'fish']);\n });\n\n it('works with horizontal layout', async () => {\n const list: SortableList = await createSorter(HORIZONTAL_LIST);\n await list.updateComplete;\n\n const ids = list.getIds();\n expect(ids).to.deep.equal(['red', 'blue', 'green']);\n\n // Test horizontal drag behavior\n const bounds = list.getBoundingClientRect();\n const orderChanged = oneEvent(list, CustomEventType.OrderChanged, false);\n\n // Drag the first item (red) to after the second item (blue)\n await moveMouse(bounds.left + 10, bounds.top + 10);\n await mouseDown();\n await moveMouse(bounds.left + 80, bounds.top + 10);\n await mouseUp();\n clock.runAll();\n\n const orderEvent = await orderChanged;\n expect(orderEvent.detail).to.deep.equal({\n swap: [0, 2]\n });\n });\n\n it('handles prepareGhost callback', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n let ghostPrepared = false;\n\n list.prepareGhost = (ghost: HTMLElement) => {\n ghostPrepared = true;\n ghost.style.backgroundColor = 'red';\n };\n\n const bounds = list.getBoundingClientRect();\n\n // Start dragging to trigger ghost creation\n await moveMouse(bounds.left + 20, bounds.bottom - 10);\n await mouseDown();\n await moveMouse(bounds.left + 30, bounds.bottom - 10);\n\n expect(ghostPrepared).to.be.true;\n\n // Clean up\n await mouseUp();\n clock.runAll();\n });\n\n it('drags', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n const updated = oneEvent(list, 'change', false);\n\n const bounds = list.getBoundingClientRect();\n\n await moveMouse(bounds.left + 20, bounds.bottom - 10);\n await mouseDown();\n await moveMouse(bounds.left + 20, bounds.top + 5);\n\n // should be hovered\n await assertScreenshot('list/sortable-dragging', getClip(list));\n\n // now lets drop - this will fire the order changed event\n const orderChanged = oneEvent(list, CustomEventType.OrderChanged, false);\n await mouseUp();\n clock.runAll();\n await list.updateComplete;\n clock.runAll();\n\n // we should fire an order changed event on drop\n const orderEvent = await orderChanged;\n expect(orderEvent.detail).to.deep.equal({\n swap: [1, 0]\n });\n\n await assertScreenshot('list/sortable-dropped', getClip(list));\n\n // we should fire a change event\n const changeEvent = await updated;\n expect(changeEvent.type).to.equal('change');\n });\n\n it('detects external drag when dragging outside container', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n list.externalDrag = true;\n await list.updateComplete;\n\n const bounds = list.getBoundingClientRect();\n\n // track external drag events\n let externalDragFired = false;\n let internalDragFired = false;\n\n list.addEventListener(CustomEventType.DragExternal, () => {\n externalDragFired = true;\n });\n\n list.addEventListener(CustomEventType.DragInternal, () => {\n internalDragFired = true;\n });\n\n // start dragging an item\n await moveMouse(bounds.left + 20, bounds.bottom - 10);\n await mouseDown();\n\n // drag outside the container (far to the right)\n await moveMouse(bounds.right + 100, bounds.top + 10);\n clock.runAll();\n\n // should have fired external drag event\n expect(externalDragFired).to.be.true;\n\n // drag back inside\n externalDragFired = false; // reset\n await moveMouse(bounds.left + 20, bounds.top + 10);\n clock.runAll();\n\n // should have fired internal drag event\n expect(internalDragFired).to.be.true;\n\n // clean up\n await mouseUp();\n clock.runAll();\n });\n\n it('fires DragStop with isExternal=true when dropped outside container', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n list.externalDrag = true;\n await list.updateComplete;\n\n const bounds = list.getBoundingClientRect();\n\n // start dragging an item\n await moveMouse(bounds.left + 20, bounds.bottom - 10);\n await mouseDown();\n\n // drag outside the container\n await moveMouse(bounds.right + 100, bounds.top + 10);\n clock.runAll();\n\n // listen for drag stop event\n const dragStop = oneEvent(list, CustomEventType.DragStop, false);\n\n // drop outside\n await mouseUp();\n clock.runAll();\n\n const dragStopEvent = await dragStop;\n expect(dragStopEvent.detail.isExternal).to.be.true;\n });\n\n describe('zoom-aware dimensions', () => {\n it('stores layout dimensions on mousedown via offsetWidth/offsetHeight', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n await list.updateComplete;\n\n const bounds = list.getBoundingClientRect();\n\n // Start drag\n await moveMouse(bounds.left + 20, bounds.bottom - 10);\n await mouseDown();\n\n // originalLayoutSize should be set using offsetWidth/offsetHeight\n expect(list.originalLayoutSize).to.not.be.null;\n // At zoom=1.0, layout size equals viewport size\n expect(list.originalLayoutSize.width).to.equal(\n list.originalElementRect.width\n );\n expect(list.originalLayoutSize.height).to.equal(\n list.originalElementRect.height\n );\n\n await mouseUp();\n clock.runAll();\n });\n\n it('ghost uses scale(1.03) without ancestor transform', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n await list.updateComplete;\n\n const bounds = list.getBoundingClientRect();\n\n // Start drag past threshold to create ghost\n await moveMouse(bounds.left + 20, bounds.bottom - 10);\n await mouseDown();\n await moveMouse(bounds.left + 30, bounds.bottom - 10);\n clock.runAll();\n\n expect(list.ghostElement).to.not.be.null;\n expect(list.ghostElement.style.transform).to.equal('scale(1.03)');\n // transformOrigin should NOT be set to '0 0' when there's no ancestor scale\n expect(list.ghostElement.style.transformOrigin).to.not.equal('0 0');\n\n await mouseUp();\n clock.runAll();\n });\n\n it('detects ancestor scale and applies it to ghost', () => {\n // Unit test the ghost scaling logic directly:\n // When originalElementRect (viewport) differs from originalLayoutSize (layout),\n // the ancestor scale is detected and applied to the ghost.\n const list = new SortableList();\n\n // Simulate being inside a container with transform: scale(0.5)\n // Layout dimensions: 100x20, viewport dimensions: 50x10\n list.originalElementRect = {\n width: 50,\n height: 10,\n left: 0,\n top: 0,\n right: 50,\n bottom: 10,\n x: 0,\n y: 0,\n toJSON: () => ({})\n } as DOMRect;\n list.originalLayoutSize = { width: 100, height: 20 };\n\n // Calculate ancestor scale the same way the component does\n const ancestorScale =\n list.originalLayoutSize.width > 0\n ? list.originalElementRect.width / list.originalLayoutSize.width\n : 1;\n const hasAncestorScale = Math.abs(ancestorScale - 1) > 0.001;\n\n expect(hasAncestorScale).to.be.true;\n expect(ancestorScale).to.equal(0.5);\n\n // The ghost would get: transform: scale(0.5 * 1.03) = scale(0.515)\n const expectedScale = ancestorScale * 1.03;\n expect(expectedScale).to.be.closeTo(0.515, 0.001);\n });\n\n it('uses originalLayoutSize for placeholder sizing', () => {\n // Verify that the component stores separate layout vs viewport dimensions\n const list = new SortableList();\n\n // At zoom=1.0, both should be the same\n const mockRect = {\n width: 100,\n height: 20,\n left: 0,\n top: 0,\n right: 100,\n bottom: 20,\n x: 0,\n y: 0,\n toJSON: () => ({})\n } as DOMRect;\n\n list.originalElementRect = mockRect;\n list.originalLayoutSize = { width: 100, height: 20 };\n\n // At zoom=1.0, they match\n expect(list.originalLayoutSize.width).to.equal(\n list.originalElementRect.width\n );\n\n // At zoom=0.5, viewport rect would be half but layout stays the same\n list.originalElementRect = {\n ...mockRect,\n width: 50,\n height: 10\n } as DOMRect;\n\n // Layout size is independent of zoom\n expect(list.originalLayoutSize.width).to.equal(100);\n expect(list.originalLayoutSize.height).to.equal(20);\n });\n });\n});\n"]}
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/Icons.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-duplicate-enum-values */
|
|
2
2
|
// for cache busting we dynamically generate a fingerprint, use yarn svg to update
|
|
3
|
-
export const SVG_FINGERPRINT = '
|
|
3
|
+
export const SVG_FINGERPRINT = 'de580a861152b5d164592ae63b65e5aa';
|
|
4
4
|
|
|
5
5
|
// only icons below are included in the sprite sheet
|
|
6
6
|
export enum Icon {
|
|
@@ -198,6 +198,8 @@ export enum Icon {
|
|
|
198
198
|
video = 'video-recorder',
|
|
199
199
|
webhook = 'link-external-01',
|
|
200
200
|
workspace = 'folder',
|
|
201
|
+
zoom_fit = 'maximize-02',
|
|
202
|
+
zoom_in = 'expand-06',
|
|
201
203
|
|
|
202
204
|
// channel types
|
|
203
205
|
channel_a = 'channel-android',
|
package/src/display/Button.ts
CHANGED
|
@@ -46,7 +46,7 @@ export class Button extends LitElement {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
.secondary-button:hover .button-mask {
|
|
49
|
-
border: 1px solid
|
|
49
|
+
border: 1px solid rgba(0, 0, 0, 0.15);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
.button-mask:hover {
|
|
@@ -100,7 +100,7 @@ export class Button extends LitElement {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
.secondary-button.active-button .button-mask {
|
|
103
|
-
border:
|
|
103
|
+
border: 1px solid transparent;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
.button-container.secondary-button.active-button:focus .button-mask {
|
|
@@ -104,7 +104,7 @@ export class FloatingTab extends RapidElement {
|
|
|
104
104
|
FloatingTab.updateAllPositions();
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
public static updateAllPositions() {
|
|
108
108
|
const sorted = [...FloatingTab.allTabs].sort((a, b) => a.order - b.order);
|
|
109
109
|
sorted.forEach((tab, index) => {
|
|
110
110
|
tab.top =
|
package/src/flow/CanvasMenu.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { CustomEventType } from '../interfaces';
|
|
|
7
7
|
* Event detail for canvas menu selection
|
|
8
8
|
*/
|
|
9
9
|
export interface CanvasMenuSelection {
|
|
10
|
-
action: 'sticky' | 'action' | 'split';
|
|
10
|
+
action: 'sticky' | 'action' | 'split' | 'reflow';
|
|
11
11
|
position: { x: number; y: number };
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -90,6 +90,9 @@ export class CanvasMenu extends RapidElement {
|
|
|
90
90
|
@property({ type: Boolean })
|
|
91
91
|
public showStickyNote = true;
|
|
92
92
|
|
|
93
|
+
@property({ type: Boolean })
|
|
94
|
+
public showReflow = false;
|
|
95
|
+
|
|
93
96
|
@state()
|
|
94
97
|
private clickPosition = { x: 0, y: 0 };
|
|
95
98
|
|
|
@@ -127,12 +130,14 @@ export class CanvasMenu extends RapidElement {
|
|
|
127
130
|
x: number,
|
|
128
131
|
y: number,
|
|
129
132
|
clickPosition: { x: number; y: number },
|
|
130
|
-
showStickyNote: boolean = true
|
|
133
|
+
showStickyNote: boolean = true,
|
|
134
|
+
showReflow: boolean = false
|
|
131
135
|
) {
|
|
132
136
|
this.x = x;
|
|
133
137
|
this.y = y;
|
|
134
138
|
this.clickPosition = clickPosition;
|
|
135
139
|
this.showStickyNote = showStickyNote;
|
|
140
|
+
this.showReflow = showReflow;
|
|
136
141
|
this.open = true;
|
|
137
142
|
|
|
138
143
|
// Adjust position after menu renders to ensure it fits on screen
|
|
@@ -180,7 +185,9 @@ export class CanvasMenu extends RapidElement {
|
|
|
180
185
|
}
|
|
181
186
|
}
|
|
182
187
|
|
|
183
|
-
private handleMenuItemClick(
|
|
188
|
+
private handleMenuItemClick(
|
|
189
|
+
action: 'sticky' | 'action' | 'split' | 'reflow'
|
|
190
|
+
) {
|
|
184
191
|
this.fireCustomEvent(CustomEventType.Selection, {
|
|
185
192
|
action,
|
|
186
193
|
position: this.clickPosition
|
|
@@ -238,6 +245,24 @@ export class CanvasMenu extends RapidElement {
|
|
|
238
245
|
</div>
|
|
239
246
|
`
|
|
240
247
|
: ''}
|
|
248
|
+
${this.showReflow
|
|
249
|
+
? html`
|
|
250
|
+
<div class="divider"></div>
|
|
251
|
+
|
|
252
|
+
<div
|
|
253
|
+
class="menu-item"
|
|
254
|
+
@click=${() => this.handleMenuItemClick('reflow')}
|
|
255
|
+
>
|
|
256
|
+
<temba-icon name="flow" size="1.25"></temba-icon>
|
|
257
|
+
<div class="menu-item-content">
|
|
258
|
+
<div class="menu-item-title">Reflow</div>
|
|
259
|
+
<div class="menu-item-description">
|
|
260
|
+
Auto-arrange nodes in this flow
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
`
|
|
265
|
+
: ''}
|
|
241
266
|
</div>
|
|
242
267
|
`;
|
|
243
268
|
}
|
package/src/flow/CanvasNode.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { Action, Exit, Node, NodeUI, Router } from '../store/flow-definition';
|
|
|
6
6
|
import { property } from 'lit/decorators.js';
|
|
7
7
|
import { RapidElement } from '../RapidElement';
|
|
8
8
|
import { getClasses } from '../utils';
|
|
9
|
+
import { isRightClick } from './utils';
|
|
9
10
|
import { Plumber } from './Plumber';
|
|
10
11
|
import { getStore } from '../store/Store';
|
|
11
12
|
import { CustomEventType } from '../interfaces';
|
|
@@ -873,8 +874,8 @@ export class CanvasNode extends RapidElement {
|
|
|
873
874
|
const actionElement = this.querySelector(`#${actionId}`) as HTMLElement;
|
|
874
875
|
|
|
875
876
|
if (actionElement) {
|
|
876
|
-
|
|
877
|
-
this.draggedActionHeight =
|
|
877
|
+
// Use offsetHeight (unaffected by ancestor CSS transforms like zoom)
|
|
878
|
+
this.draggedActionHeight = actionElement.offsetHeight;
|
|
878
879
|
} else {
|
|
879
880
|
// Fallback to a reasonable default
|
|
880
881
|
this.draggedActionHeight = 60;
|
|
@@ -970,6 +971,8 @@ export class CanvasNode extends RapidElement {
|
|
|
970
971
|
}
|
|
971
972
|
|
|
972
973
|
private handleActionMouseDown(event: MouseEvent, action: Action): void {
|
|
974
|
+
if (isRightClick(event)) return;
|
|
975
|
+
|
|
973
976
|
// Don't handle clicks on the remove button, drag handle, or when action is in removing state
|
|
974
977
|
const target = event.target as HTMLElement;
|
|
975
978
|
if (
|
|
@@ -1097,6 +1100,8 @@ export class CanvasNode extends RapidElement {
|
|
|
1097
1100
|
}
|
|
1098
1101
|
|
|
1099
1102
|
private handleNodeMouseDown(event: MouseEvent): void {
|
|
1103
|
+
if (isRightClick(event)) return;
|
|
1104
|
+
|
|
1100
1105
|
// Don't handle clicks on the remove button, exits, drag handle, or when node is in removing state
|
|
1101
1106
|
const target = event.target as HTMLElement;
|
|
1102
1107
|
if (
|