@nyaruka/temba-components 0.137.0 → 0.138.4

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 (88) hide show
  1. package/.devcontainer/Dockerfile +0 -9
  2. package/.devcontainer/devcontainer.json +8 -3
  3. package/.github/workflows/build.yml +6 -1
  4. package/.github/workflows/cla.yml +1 -1
  5. package/.github/workflows/publish.yml +6 -1
  6. package/CHANGELOG.md +42 -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 +445 -278
  16. package/dist/temba-components.js.map +1 -1
  17. package/out-tsc/src/display/FloatingTab.js +16 -8
  18. package/out-tsc/src/display/FloatingTab.js.map +1 -1
  19. package/out-tsc/src/flow/CanvasMenu.js +33 -15
  20. package/out-tsc/src/flow/CanvasMenu.js.map +1 -1
  21. package/out-tsc/src/flow/CanvasNode.js +49 -24
  22. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  23. package/out-tsc/src/flow/Editor.js +583 -70
  24. package/out-tsc/src/flow/Editor.js.map +1 -1
  25. package/out-tsc/src/flow/NodeTypeSelector.js +13 -11
  26. package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -1
  27. package/out-tsc/src/flow/Plumber.js +110 -64
  28. package/out-tsc/src/flow/Plumber.js.map +1 -1
  29. package/out-tsc/src/flow/actions/set_contact_field.js +5 -1
  30. package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
  31. package/out-tsc/src/list/RunList.js +2 -1
  32. package/out-tsc/src/list/RunList.js.map +1 -1
  33. package/out-tsc/src/list/TicketList.js +2 -1
  34. package/out-tsc/src/list/TicketList.js.map +1 -1
  35. package/out-tsc/src/locales/es.js +5 -5
  36. package/out-tsc/src/locales/es.js.map +1 -1
  37. package/out-tsc/src/locales/fr.js +5 -5
  38. package/out-tsc/src/locales/fr.js.map +1 -1
  39. package/out-tsc/src/locales/locale-codes.js +11 -2
  40. package/out-tsc/src/locales/locale-codes.js.map +1 -1
  41. package/out-tsc/src/locales/pt.js +5 -5
  42. package/out-tsc/src/locales/pt.js.map +1 -1
  43. package/out-tsc/src/simulator/Simulator.js +11 -4
  44. package/out-tsc/src/simulator/Simulator.js.map +1 -1
  45. package/out-tsc/src/store/AppState.js +17 -2
  46. package/out-tsc/src/store/AppState.js.map +1 -1
  47. package/out-tsc/test/temba-contact-fields.test.js +3 -3
  48. package/out-tsc/test/temba-contact-fields.test.js.map +1 -1
  49. package/out-tsc/test/temba-flow-editor-node.test.js +3 -1
  50. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
  51. package/out-tsc/test/temba-flow-editor-revisions.test.js +106 -0
  52. package/out-tsc/test/temba-flow-editor-revisions.test.js.map +1 -0
  53. package/out-tsc/test/temba-flow-editor.test.js +14 -10
  54. package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
  55. package/out-tsc/test/temba-flow-plumber-connections.test.js +7 -1
  56. package/out-tsc/test/temba-flow-plumber-connections.test.js.map +1 -1
  57. package/out-tsc/test/temba-flow-plumber.test.js +6 -0
  58. package/out-tsc/test/temba-flow-plumber.test.js.map +1 -1
  59. package/out-tsc/test/temba-select.test.js +1 -0
  60. package/out-tsc/test/temba-select.test.js.map +1 -1
  61. package/package.json +1 -1
  62. package/screenshots/truth/floating-tab/gray.png +0 -0
  63. package/screenshots/truth/floating-tab/green.png +0 -0
  64. package/screenshots/truth/floating-tab/purple.png +0 -0
  65. package/screenshots/truth/node-type-selector/action-mode.png +0 -0
  66. package/screenshots/truth/node-type-selector/split-mode.png +0 -0
  67. package/src/display/FloatingTab.ts +18 -8
  68. package/src/flow/CanvasMenu.ts +38 -16
  69. package/src/flow/CanvasNode.ts +62 -29
  70. package/src/flow/Editor.ts +699 -74
  71. package/src/flow/NodeTypeSelector.ts +13 -11
  72. package/src/flow/Plumber.ts +123 -69
  73. package/src/flow/actions/set_contact_field.ts +5 -1
  74. package/src/list/RunList.ts +2 -1
  75. package/src/list/TicketList.ts +2 -1
  76. package/src/locales/es.ts +18 -13
  77. package/src/locales/fr.ts +18 -13
  78. package/src/locales/locale-codes.ts +11 -2
  79. package/src/locales/pt.ts +18 -13
  80. package/src/simulator/Simulator.ts +11 -5
  81. package/src/store/AppState.ts +18 -2
  82. package/test/temba-contact-fields.test.ts +8 -3
  83. package/test/temba-flow-editor-node.test.ts +3 -1
  84. package/test/temba-flow-editor-revisions.test.ts +134 -0
  85. package/test/temba-flow-editor.test.ts +16 -10
  86. package/test/temba-flow-plumber-connections.test.ts +7 -1
  87. package/test/temba-flow-plumber.test.ts +6 -0
  88. package/test/temba-select.test.ts +1 -0
@@ -172,12 +172,13 @@ export class NodeTypeSelector extends RapidElement {
172
172
 
173
173
  .items-grid {
174
174
  display: grid;
175
- grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
175
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
176
176
  gap: 0.75em;
177
177
  }
178
178
 
179
179
  .node-item {
180
- padding: 1em;
180
+ padding: 0.5em;
181
+ padding-left: 1em;
181
182
  border: 1px solid rgba(0, 0, 0, 0.1);
182
183
  border-radius: calc(var(--curvature) * 0.75);
183
184
  cursor: pointer;
@@ -211,7 +212,6 @@ export class NodeTypeSelector extends RapidElement {
211
212
  font-weight: 500;
212
213
  font-size: 1rem;
213
214
  color: var(--color-text-dark);
214
- margin-bottom: 0.25em;
215
215
  }
216
216
 
217
217
  .node-item-type {
@@ -259,8 +259,14 @@ export class NodeTypeSelector extends RapidElement {
259
259
  this.open = true;
260
260
  }
261
261
 
262
- public close() {
263
- this.open = false;
262
+ public close(fireCanceledEvent: boolean = true) {
263
+ if (this.open) {
264
+ this.open = false;
265
+ // Fire canceled event so parent can clean up, but only if not from a selection
266
+ if (fireCanceledEvent) {
267
+ this.fireCustomEvent(CustomEventType.Canceled, {});
268
+ }
269
+ }
264
270
  }
265
271
 
266
272
  /**
@@ -297,7 +303,8 @@ export class NodeTypeSelector extends RapidElement {
297
303
  nodeType,
298
304
  position: this.clickPosition
299
305
  } as NodeTypeSelection);
300
- this.close();
306
+ // Close without firing canceled event since we made a selection
307
+ this.close(false);
301
308
  }
302
309
 
303
310
  private handleOverlayClick() {
@@ -549,7 +556,6 @@ export class NodeTypeSelector extends RapidElement {
549
556
  <div class="node-item-title">
550
557
  ${item.config.name}
551
558
  </div>
552
- <div class="node-item-type">${item.type}</div>
553
559
  </div>
554
560
  `
555
561
  )}
@@ -587,9 +593,6 @@ export class NodeTypeSelector extends RapidElement {
587
593
  <div class="node-item-title">
588
594
  ${item.config.name}
589
595
  </div>
590
- <div class="node-item-type">
591
- ${item.type}
592
- </div>
593
596
  </div>
594
597
  `
595
598
  )}
@@ -622,7 +625,6 @@ export class NodeTypeSelector extends RapidElement {
622
625
  <div class="node-item-title">
623
626
  ${item.config.name}
624
627
  </div>
625
- <div class="node-item-type">${item.type}</div>
626
628
  </div>
627
629
  `
628
630
  )}
@@ -89,50 +89,54 @@ export class Plumber {
89
89
  private showContactsTimeout: number | null = null;
90
90
  private editor: any;
91
91
 
92
- constructor(canvas: HTMLElement, editor: any) {
93
- this.editor = editor;
94
- ready(() => {
95
- this.jsPlumb = newInstance({
96
- container: canvas,
97
- connectionsDetachable: true,
98
- endpointStyle: {
99
- fill: 'green'
100
- },
101
- connector: CONNECTOR_DEFAULTS,
102
- connectionOverlays: OVERLAYS_DEFAULTS
103
- });
92
+ initializeJSPlumb(canvas: HTMLElement) {
93
+ this.jsPlumb = newInstance({
94
+ container: canvas,
95
+ connectionsDetachable: true,
96
+ endpointStyle: {
97
+ fill: 'green'
98
+ },
99
+ connector: CONNECTOR_DEFAULTS,
100
+ connectionOverlays: OVERLAYS_DEFAULTS
101
+ });
104
102
 
105
- // Bind to connection events
106
- this.jsPlumb.bind(EVENT_CONNECTION, (info) => {
107
- this.connectionDragging = false;
108
- this.notifyListeners(EVENT_CONNECTION, info);
109
- });
103
+ // Bind to connection events
104
+ this.jsPlumb.bind(EVENT_CONNECTION, (info) => {
105
+ this.connectionDragging = false;
106
+ this.notifyListeners(EVENT_CONNECTION, info);
107
+ });
110
108
 
111
- // Bind to connection drag events
112
- this.jsPlumb.bind(EVENT_CONNECTION_DRAG, (info) => {
113
- this.connectionDragging = true;
114
- this.notifyListeners(EVENT_CONNECTION_DRAG, info);
115
- });
109
+ // Bind to connection drag events
110
+ this.jsPlumb.bind(EVENT_CONNECTION_DRAG, (info) => {
111
+ this.connectionDragging = true;
112
+ this.notifyListeners(EVENT_CONNECTION_DRAG, info);
113
+ });
116
114
 
117
- this.jsPlumb.bind(EVENT_CONNECTION_ABORT, (info) => {
118
- this.connectionDragging = false;
119
- this.notifyListeners(EVENT_CONNECTION_ABORT, info);
120
- });
115
+ this.jsPlumb.bind(EVENT_CONNECTION_ABORT, (info) => {
116
+ this.connectionDragging = false;
117
+ this.notifyListeners(EVENT_CONNECTION_ABORT, info);
118
+ });
121
119
 
122
- this.jsPlumb.bind(EVENT_CONNECTION_DETACHED, (info) => {
123
- this.connectionDragging = false;
124
- this.notifyListeners(EVENT_CONNECTION_DETACHED, info);
125
- });
120
+ this.jsPlumb.bind(EVENT_CONNECTION_DETACHED, (info) => {
121
+ this.connectionDragging = false;
122
+ this.notifyListeners(EVENT_CONNECTION_DETACHED, info);
123
+ });
126
124
 
127
- this.jsPlumb.bind(EVENT_REVERT, (info) => {
128
- this.notifyListeners(EVENT_REVERT, info);
129
- });
125
+ this.jsPlumb.bind(EVENT_REVERT, (info) => {
126
+ this.notifyListeners(EVENT_REVERT, info);
127
+ });
130
128
 
131
- this.jsPlumb.bind(INTERCEPT_BEFORE_DROP, () => {
132
- // we always deny automatic connections
133
- return false;
134
- });
135
- this.jsPlumb.bind(INTERCEPT_BEFORE_DETACH, () => {});
129
+ this.jsPlumb.bind(INTERCEPT_BEFORE_DROP, () => {
130
+ // we always deny automatic connections
131
+ return false;
132
+ });
133
+ this.jsPlumb.bind(INTERCEPT_BEFORE_DETACH, () => {});
134
+ }
135
+
136
+ constructor(canvas: HTMLElement, editor: any) {
137
+ this.editor = editor;
138
+ ready(() => {
139
+ this.initializeJSPlumb(canvas);
136
140
  });
137
141
  }
138
142
 
@@ -159,12 +163,14 @@ export class Plumber {
159
163
 
160
164
  public makeTarget(uuid: string) {
161
165
  const element = document.getElementById(uuid);
162
- this.jsPlumb.addEndpoint(element, TARGET_DEFAULTS);
166
+ if (!element) return;
167
+ return this.jsPlumb.addEndpoint(element, TARGET_DEFAULTS);
163
168
  }
164
169
 
165
170
  public makeSource(uuid: string) {
166
171
  const element = document.getElementById(uuid);
167
- this.jsPlumb.addEndpoint(element, SOURCE_DEFAULTS);
172
+ if (!element) return;
173
+ return this.jsPlumb.addEndpoint(element, SOURCE_DEFAULTS);
168
174
  }
169
175
 
170
176
  // we'll process our pending connections, but we want to debounce this
@@ -180,27 +186,40 @@ export class Plumber {
180
186
  this.jsPlumb.batch(() => {
181
187
  this.pendingConnections.forEach((connection) => {
182
188
  const { scope, fromId, toId } = connection;
183
- const fromElement = document.getElementById(fromId);
184
- const toElement = document.getElementById(toId);
185
-
186
- // delete any existing endpoints
187
- this.jsPlumb.selectEndpoints({ source: fromId }).deleteAll();
188
-
189
- const source = this.jsPlumb.addEndpoint(fromElement, {
190
- ...SOURCE_DEFAULTS,
191
- endpoint: {
192
- ...SOURCE_DEFAULTS.endpoint,
193
- options: {
194
- ...SOURCE_DEFAULTS.endpoint.options,
195
- cssClass: 'plumb-source connected'
196
- }
197
- }
198
- });
199
189
 
200
- const target = this.jsPlumb.addEndpoint(toElement, TARGET_DEFAULTS);
190
+ // sources and targets must exist
191
+ const source = document.getElementById(fromId);
192
+ // const target = document.getElementById(toId);
193
+
194
+ this.revalidate([fromId, toId]);
195
+
196
+ // we need to find the source endpoint
197
+ const sourceEndpoint = this.jsPlumb
198
+ .getEndpoints(source)
199
+ ?.find((endpoint) =>
200
+ endpoint.elementId === fromId ? true : false
201
+ );
202
+
203
+ // update endpoint have connect css class
204
+ if (sourceEndpoint) {
205
+ sourceEndpoint.addClass('connected');
206
+ }
207
+
208
+ // each connection needs its own target endpoint
209
+ const targetEndpoint = this.makeTarget(toId);
210
+
211
+ if (!source || !targetEndpoint) {
212
+ console.warn(
213
+ `Plumber: Cannot connect ${fromId} to ${toId}. Element(s) missing.`
214
+ );
215
+ return;
216
+ }
217
+
218
+ // delete connections
219
+ this.jsPlumb.select({ source, targetEndpoint }).deleteAll();
201
220
  this.jsPlumb.connect({
202
- source,
203
- target,
221
+ source: source,
222
+ target: targetEndpoint,
204
223
  connector: {
205
224
  ...CONNECTOR_DEFAULTS,
206
225
  options: { ...CONNECTOR_DEFAULTS.options, gap: [0, 5] }
@@ -212,7 +231,15 @@ export class Plumber {
212
231
  });
213
232
  this.pendingConnections = [];
214
233
  });
215
- }, 50);
234
+
235
+ // Force a repaint to ensure connections are positioned correctly
236
+ // especially after bulk updates or view switching
237
+ window.requestAnimationFrame(() => {
238
+ if (this.jsPlumb) {
239
+ this.jsPlumb.repaintEverything();
240
+ }
241
+ });
242
+ }, 0);
216
243
  }
217
244
 
218
245
  public connectIds(scope: string, fromId: string, toId: string) {
@@ -597,20 +624,44 @@ export class Plumber {
597
624
  });
598
625
  }
599
626
 
600
- public removeNodeConnections(nodeId: string) {
627
+ public reset() {
628
+ if (this.connectionWait) {
629
+ clearTimeout(this.connectionWait);
630
+ this.connectionWait = null;
631
+ }
632
+ this.pendingConnections = [];
633
+ this.jsPlumb.select().deleteAll();
634
+ this.jsPlumb._managedElements = {};
635
+ }
636
+
637
+ public forgetNode(nodeId: string) {
638
+ if (!this.jsPlumb) return;
639
+ const element = document.getElementById(nodeId);
640
+ if (!element) return;
641
+
642
+ this.jsPlumb.deleteConnectionsForElement(element);
643
+ this.jsPlumb.removeAllEndpoints(element);
644
+ this.jsPlumb.unmanage(element);
645
+ }
646
+
647
+ public removeNodeConnections(nodeId: string, exitIds?: string[]) {
601
648
  if (!this.jsPlumb) return;
602
649
 
603
650
  const inbound = this.jsPlumb.select({ target: nodeId });
604
- const exitIds =
651
+
652
+ // Use provided exitIds or try to find them in DOM (fallback)
653
+ const exits =
654
+ exitIds ||
605
655
  Array.from(
606
656
  document.getElementById(nodeId)?.querySelectorAll('.exit') || []
607
657
  ).map((exit) => {
608
658
  return exit.id;
609
- }) || [];
659
+ }) ||
660
+ [];
610
661
 
611
662
  inbound.deleteAll();
612
- this.jsPlumb.select({ source: exitIds }).deleteAll();
613
- this.jsPlumb.selectEndpoints({ source: exitIds }).deleteAll();
663
+ this.jsPlumb.select({ source: exits }).deleteAll();
664
+ this.jsPlumb.selectEndpoints({ source: exits }).deleteAll();
614
665
  }
615
666
 
616
667
  public removeExitConnection(exitId: string) {
@@ -627,13 +678,16 @@ export class Plumber {
627
678
  this.jsPlumb.deleteConnection(connection);
628
679
  });
629
680
 
630
- // Re-create the source endpoint (now without connection)
631
- this.jsPlumb.removeAllEndpoints(exitElement);
632
- this.makeSource(exitId);
633
-
634
681
  return connections.length > 0;
635
682
  }
636
683
 
684
+ public removeAllEndpoints(nodeId: string) {
685
+ if (!this.jsPlumb) return;
686
+ const element = document.getElementById(nodeId);
687
+ if (!element) return;
688
+ this.jsPlumb.removeAllEndpoints(element, true);
689
+ }
690
+
637
691
  /**
638
692
  * Set the removing state for an exit's connection
639
693
  * @param exitId The ID of the exit whose connections should be marked as removing
@@ -29,7 +29,11 @@ export const set_contact_field: ActionConfig = {
29
29
  endpoint: '/api/v2/fields.json',
30
30
  helpText: 'Select the contact field to update',
31
31
  allowCreate: true,
32
- createArbitraryOption: (input: string) => ({ key: input, name: input })
32
+ createArbitraryOption: (input: string) => ({
33
+ key: input,
34
+ name: input,
35
+ type: 'text'
36
+ })
33
37
  },
34
38
  value: {
35
39
  type: 'text',
@@ -179,7 +179,8 @@ export class RunList extends TembaList {
179
179
  public getRefreshEndpoint() {
180
180
  if (this.items.length > 0) {
181
181
  const modifiedOn = this.items[0].modified_on;
182
- return this.endpoint + '&after=' + modifiedOn;
182
+ const separator = this.endpoint.includes('?') ? '&' : '?';
183
+ return this.endpoint + separator + 'after=' + modifiedOn;
183
184
  }
184
185
  return this.endpoint;
185
186
  }
@@ -11,8 +11,9 @@ export class TicketList extends TembaList {
11
11
  public getRefreshEndpoint() {
12
12
  if (this.items.length > 0) {
13
13
  const lastActivity = this.items[0].ticket.last_activity_on;
14
+ const separator = this.endpoint.includes('?') ? '&' : '?';
14
15
  return (
15
- this.endpoint + '?after=' + new Date(lastActivity).getTime() * 1000
16
+ this.endpoint + separator + 'after=' + new Date(lastActivity).getTime() * 1000
16
17
  );
17
18
  }
18
19
  return this.endpoint;
package/src/locales/es.ts CHANGED
@@ -1,13 +1,18 @@
1
- // Do not modify this file by hand!
2
- // Re-generate this file by running lit-localize
3
-
4
- /* eslint-disable no-irregular-whitespace */
5
- /* eslint-disable @typescript-eslint/no-explicit-any */
6
-
7
- export const templates = {
8
- scf1453991c986b25: `Tab para completar, enter para seleccionar`,
9
- s73b4d70c02f4b4e0: `No options`,
10
- s8f02e3a18ffc083a: `Are not currently in a flow`,
11
- s638236250662c6b3: `Have sent a message in the last`,
12
- s4788ee206c4570c7: `Have not started this flow in the last 90 days`
13
- };
1
+
2
+ // Do not modify this file by hand!
3
+ // Re-generate this file by running lit-localize
4
+
5
+
6
+
7
+
8
+ /* eslint-disable no-irregular-whitespace */
9
+ /* eslint-disable @typescript-eslint/no-explicit-any */
10
+
11
+ export const templates = {
12
+ 'scf1453991c986b25': `Tab para completar, enter para seleccionar`,
13
+ 's73b4d70c02f4b4e0': `No options`,
14
+ 's8f02e3a18ffc083a': `Are not currently in a flow`,
15
+ 's638236250662c6b3': `Have sent a message in the last`,
16
+ 's4788ee206c4570c7': `Have not started this flow in the last 90 days`,
17
+ };
18
+
package/src/locales/fr.ts CHANGED
@@ -1,13 +1,18 @@
1
- // Do not modify this file by hand!
2
- // Re-generate this file by running lit-localize
3
-
4
- /* eslint-disable no-irregular-whitespace */
5
- /* eslint-disable @typescript-eslint/no-explicit-any */
6
-
7
- export const templates = {
8
- s73b4d70c02f4b4e0: `No options`,
9
- scf1453991c986b25: `Tab to complete, enter to select`,
10
- s8f02e3a18ffc083a: `Are not currently in a flow`,
11
- s638236250662c6b3: `Have sent a message in the last`,
12
- s4788ee206c4570c7: `Have not started this flow in the last 90 days`
13
- };
1
+
2
+ // Do not modify this file by hand!
3
+ // Re-generate this file by running lit-localize
4
+
5
+
6
+
7
+
8
+ /* eslint-disable no-irregular-whitespace */
9
+ /* eslint-disable @typescript-eslint/no-explicit-any */
10
+
11
+ export const templates = {
12
+ 's73b4d70c02f4b4e0': `No options`,
13
+ 'scf1453991c986b25': `Tab to complete, enter to select`,
14
+ 's8f02e3a18ffc083a': `Are not currently in a flow`,
15
+ 's638236250662c6b3': `Have sent a message in the last`,
16
+ 's4788ee206c4570c7': `Have not started this flow in the last 90 days`,
17
+ };
18
+
@@ -10,9 +10,18 @@ export const sourceLocale = `en`;
10
10
  * The other locale codes that this application is localized into. Sorted
11
11
  * lexicographically.
12
12
  */
13
- export const targetLocales = [`es`, `fr`, `pt`] as const;
13
+ export const targetLocales = [
14
+ `es`,
15
+ `fr`,
16
+ `pt`,
17
+ ] as const;
14
18
 
15
19
  /**
16
20
  * All valid project locale codes. Sorted lexicographically.
17
21
  */
18
- export const allLocales = [`en`, `es`, `fr`, `pt`] as const;
22
+ export const allLocales = [
23
+ `en`,
24
+ `es`,
25
+ `fr`,
26
+ `pt`,
27
+ ] as const;
package/src/locales/pt.ts CHANGED
@@ -1,13 +1,18 @@
1
- // Do not modify this file by hand!
2
- // Re-generate this file by running lit-localize
3
-
4
- /* eslint-disable no-irregular-whitespace */
5
- /* eslint-disable @typescript-eslint/no-explicit-any */
6
-
7
- export const templates = {
8
- s73b4d70c02f4b4e0: `No options`,
9
- scf1453991c986b25: `Tab to complete, enter to select`,
10
- s8f02e3a18ffc083a: `Are not currently in a flow`,
11
- s638236250662c6b3: `Have sent a message in the last`,
12
- s4788ee206c4570c7: `Have not started this flow in the last 90 days`
13
- };
1
+
2
+ // Do not modify this file by hand!
3
+ // Re-generate this file by running lit-localize
4
+
5
+
6
+
7
+
8
+ /* eslint-disable no-irregular-whitespace */
9
+ /* eslint-disable @typescript-eslint/no-explicit-any */
10
+
11
+ export const templates = {
12
+ 's73b4d70c02f4b4e0': `No options`,
13
+ 'scf1453991c986b25': `Tab to complete, enter to select`,
14
+ 's8f02e3a18ffc083a': `Are not currently in a flow`,
15
+ 's638236250662c6b3': `Have sent a message in the last`,
16
+ 's4788ee206c4570c7': `Have not started this flow in the last 90 days`,
17
+ };
18
+
@@ -183,7 +183,6 @@ export class Simulator extends RapidElement {
183
183
  backdrop-filter: blur(10px);
184
184
  border-radius: 16px;
185
185
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
186
- pointer-events: all;
187
186
  }
188
187
  .option-btn {
189
188
  background: rgba(255, 255, 255, 0.1);
@@ -359,6 +358,10 @@ export class Simulator extends RapidElement {
359
358
  pointer-events: auto;
360
359
  }
361
360
 
361
+ .context-explorer.hidden {
362
+ pointer-events: none !important;
363
+ }
364
+
362
365
  .context-item {
363
366
  display: flex;
364
367
  align-items: flex-start;
@@ -1319,8 +1322,6 @@ export class Simulator extends RapidElement {
1319
1322
  const phoneWindow = this.shadowRoot.getElementById(
1320
1323
  'phone-window'
1321
1324
  ) as FloatingWindow;
1322
- // phoneWindow.hide();
1323
-
1324
1325
  phoneWindow.handleClose();
1325
1326
  this.isVisible = false;
1326
1327
  getStore().getState().setSimulatorActive(false);
@@ -1753,7 +1754,9 @@ export class Simulator extends RapidElement {
1753
1754
  >
1754
1755
  <div class="phone-simulator" style="${styleVars}">
1755
1756
  <div
1756
- class="context-explorer ${this.contextExplorerOpen ? 'open' : ''}"
1757
+ class="context-explorer ${this.contextExplorerOpen
1758
+ ? 'open'
1759
+ : ''} ${this.isVisible ? 'visible' : 'hidden'}"
1757
1760
  >
1758
1761
  <div class="context-explorer-scroll">
1759
1762
  ${this.context
@@ -1884,7 +1887,10 @@ export class Simulator extends RapidElement {
1884
1887
  </div>
1885
1888
  </div>
1886
1889
  </div>
1887
- <div class="option-pane">
1890
+ <div
1891
+ class="option-pane"
1892
+ style="pointer-events:${this.isVisible ? 'all' : 'none'}"
1893
+ >
1888
1894
  <button class="option-btn" @click=${this.handleClose} title="Close">
1889
1895
  <temba-icon name="x" size="1.5"></temba-icon>
1890
1896
  </button>
@@ -105,6 +105,7 @@ export interface AppState {
105
105
  languageNames: { [code: string]: Language };
106
106
  workspace: Workspace;
107
107
  isTranslating: boolean;
108
+ viewingRevision: boolean;
108
109
 
109
110
  dirtyDate: Date | null;
110
111
 
@@ -174,6 +175,7 @@ export const zustand = createStore<AppState>()(
174
175
  flowDefinition: null,
175
176
  flowInfo: null,
176
177
  isTranslating: false,
178
+ viewingRevision: false,
177
179
  dirtyDate: null,
178
180
  activity: null,
179
181
  simulatorActivity: null,
@@ -187,6 +189,7 @@ export const zustand = createStore<AppState>()(
187
189
  },
188
190
 
189
191
  fetchRevision: async (endpoint: string, id: string = null) => {
192
+ const viewingRevision = !!id && id !== 'latest';
190
193
  if (!id) {
191
194
  id = 'latest';
192
195
  }
@@ -198,7 +201,11 @@ export const zustand = createStore<AppState>()(
198
201
  throw new Error('Network response was not ok');
199
202
  }
200
203
  const data = (await response.json()) as FlowContents;
201
- set({ flowInfo: data.info, flowDefinition: data.definition });
204
+ set({
205
+ flowInfo: data.info,
206
+ flowDefinition: data.definition,
207
+ viewingRevision
208
+ });
202
209
  },
203
210
 
204
211
  fetchWorkspace: async (endpoint) => {
@@ -282,7 +289,11 @@ export const zustand = createStore<AppState>()(
282
289
  setFlowContents: (flow: FlowContents) => {
283
290
  set((state: AppState) => {
284
291
  const flowLang = flow.definition.language;
285
- state.flowDefinition = flow.definition;
292
+ // Clone to ensure mutable for sorting
293
+ state.flowDefinition = {
294
+ ...flow.definition,
295
+ nodes: [...(flow.definition.nodes || [])]
296
+ };
286
297
  state.flowInfo = flow.info;
287
298
  // Reset to the flow's default language when loading a new flow
288
299
  state.languageCode = flowLang;
@@ -473,6 +484,11 @@ export const zustand = createStore<AppState>()(
473
484
  exitUuid: string,
474
485
  destinationNodeUuid: string
475
486
  ) => {
487
+ /* console.log('Upating connection:', {
488
+ nodeUuid,
489
+ exitUuid,
490
+ destinationNodeUuid
491
+ });*/
476
492
  set((state: AppState) => {
477
493
  // Find the exit with this UUID
478
494
  const node = state.flowDefinition.nodes.find(
@@ -1,14 +1,19 @@
1
1
  import { assert, expect } from '@open-wc/testing';
2
2
  import { ContactFields } from '../src/live/ContactFields';
3
- import { delay, getComponent, loadStore, mockPOST } from './utils.test';
3
+ import {
4
+ getComponent,
5
+ loadStore,
6
+ mockPOST,
7
+ waitForCondition
8
+ } from './utils.test';
4
9
 
5
10
  const TAG = 'temba-contact-fields';
6
11
  const getFields = async (attrs: any = {}) => {
7
12
  attrs['endpoint'] = '/test-assets/contacts/';
8
13
  const fields = (await getComponent(TAG, attrs, '', 600)) as ContactFields;
9
14
 
10
- // wait for our contact to load
11
- await delay(100);
15
+ // wait for our contact data to load
16
+ await waitForCondition(() => fields.data !== undefined);
12
17
 
13
18
  return fields;
14
19
  };
@@ -835,7 +835,9 @@ describe('EditorNode', () => {
835
835
  makeTarget: stub(),
836
836
  makeSource: stub(),
837
837
  connectIds: stub(),
838
- removeExitConnection: stub()
838
+ removeExitConnection: stub(),
839
+ forgetNode: stub(),
840
+ removeNodeConnections: stub()
839
841
  };
840
842
  editorNode['plumber'] = mockPlumber;
841
843
  });