@nyaruka/temba-components 0.167.0 → 0.168.1

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 (59) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/locales/es.js +3 -1
  3. package/dist/locales/es.js.map +1 -1
  4. package/dist/locales/fr.js +3 -1
  5. package/dist/locales/fr.js.map +1 -1
  6. package/dist/locales/pt.js +3 -1
  7. package/dist/locales/pt.js.map +1 -1
  8. package/dist/temba-components.js +2842 -1715
  9. package/dist/temba-components.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/Icons.ts +2 -0
  12. package/src/display/Button.ts +8 -6
  13. package/src/display/Chat.ts +541 -191
  14. package/src/display/Dropdown.ts +40 -17
  15. package/src/display/Label.ts +25 -4
  16. package/src/display/TembaDate.ts +11 -2
  17. package/src/display/Tip.ts +157 -18
  18. package/src/events/eventRenderers.ts +678 -268
  19. package/src/events.ts +16 -1
  20. package/src/flow/NodeEditor.ts +82 -9
  21. package/src/flow/actions/add_contact_urn.ts +1 -0
  22. package/src/flow/actions/play_audio.ts +1 -0
  23. package/src/flow/actions/send_broadcast.ts +17 -1
  24. package/src/flow/actions/send_msg.ts +12 -1
  25. package/src/flow/actions/set_run_result.ts +1 -0
  26. package/src/flow/actions/start_session.ts +10 -1
  27. package/src/flow/nodes/shared-rules.ts +2 -0
  28. package/src/flow/nodes/split_by_airtime.ts +6 -0
  29. package/src/flow/nodes/split_by_expression.ts +1 -0
  30. package/src/flow/nodes/split_by_llm.ts +2 -0
  31. package/src/flow/nodes/split_by_llm_categorize.ts +3 -1
  32. package/src/flow/nodes/split_by_random.ts +2 -1
  33. package/src/flow/nodes/split_by_resthook.ts +4 -1
  34. package/src/flow/nodes/split_by_webhook.ts +9 -1
  35. package/src/flow/types.ts +3 -0
  36. package/src/flow/utils.ts +49 -0
  37. package/src/form/DatePicker.ts +2 -1
  38. package/src/layout/Card.ts +9 -0
  39. package/src/layout/CardLayout.ts +426 -21
  40. package/src/layout/CardStack.ts +4 -1
  41. package/src/list/ContactList.ts +24 -17
  42. package/src/list/ContentList.ts +640 -33
  43. package/src/list/MsgList.ts +7 -1
  44. package/src/list/SortableList.ts +5 -1
  45. package/src/list/TembaMenu.ts +67 -6
  46. package/src/live/ContactChat.ts +655 -336
  47. package/src/live/ContactDetails.ts +961 -88
  48. package/src/live/ContactFieldEditor.ts +46 -11
  49. package/src/live/ContactFields.ts +57 -16
  50. package/src/live/ContactStoreElement.ts +15 -8
  51. package/src/locales/es.ts +3 -2
  52. package/src/locales/fr.ts +3 -2
  53. package/src/locales/pt.ts +3 -2
  54. package/src/simulator/Simulator.ts +28 -0
  55. package/src/styles/pillVariants.ts +13 -1
  56. package/web-test-runner.config.mjs +2 -0
  57. package/xliff/es.xlf +6 -3
  58. package/xliff/fr.xlf +6 -3
  59. package/xliff/pt.xlf +6 -3
@@ -114,9 +114,13 @@ export class MsgList extends ContentList<Msg> {
114
114
  key: 'contact',
115
115
  label: 'Contact',
116
116
  width: '130px',
117
- pinned: true
117
+ pinned: true,
118
+ resizable: true
118
119
  },
119
120
  { key: 'text', label: 'Message', grow: true },
121
+ // Sent is deliberately not resizable: it's the last column, so its
122
+ // only possible handle would sit on the table's outer edge (Message
123
+ // is the grow column and owns the boundary between them).
120
124
  {
121
125
  key: 'created_on',
122
126
  label: 'Sent',
@@ -130,6 +134,8 @@ export class MsgList extends ContentList<Msg> {
130
134
  icon: Icon.label,
131
135
  labelsEndpoint: '/api/v2/labels.json'
132
136
  },
137
+ // Archive trails the reversible actions; Delete, when offered,
138
+ // always goes last.
133
139
  { key: 'archive', label: 'Archive', icon: Icon.archive },
134
140
  { key: 'delete', label: 'Delete', icon: Icon.delete, destructive: true }
135
141
  ];
@@ -25,7 +25,11 @@ export class SortableList extends RapidElement {
25
25
  user-select: none;
26
26
  position: relative;
27
27
  display: grid;
28
- grid-template-columns: 1fr;
28
+ /* minmax(0, 1fr) rather than bare 1fr — an fr track's implicit
29
+ minimum is min-content, so one long unbreakable line in an
30
+ item (e.g. a flow pill's name) would widen the track past
31
+ the container instead of letting the content ellipsize */
32
+ grid-template-columns: minmax(0, 1fr);
29
33
  }
30
34
 
31
35
  .container.horizontal {
@@ -27,6 +27,7 @@ export interface MenuItem {
27
27
  items?: MenuItem[];
28
28
  inline?: boolean;
29
29
  type?: string;
30
+ target?: string;
30
31
  on_submit?: string;
31
32
  bubble?: string;
32
33
  popup?: boolean;
@@ -235,6 +236,38 @@ export class TembaMenu extends ResizeElement {
235
236
  overflow: hidden;
236
237
  }
237
238
 
239
+ /* popups holding natural links hug their content instead */
240
+ temba-dropdown > div[slot='dropdown']:has(> .popup-link) {
241
+ width: auto;
242
+ min-width: 11em;
243
+ }
244
+
245
+ .popup-link {
246
+ display: flex;
247
+ align-items: center;
248
+ margin: 0.5em 1em;
249
+ color: var(--color-link-primary);
250
+ --icon-color: var(--text-2);
251
+ cursor: pointer;
252
+ text-decoration: none;
253
+ }
254
+
255
+ .popup-link temba-icon {
256
+ margin-right: 0.5em;
257
+ }
258
+
259
+ .popup-link:hover {
260
+ text-decoration: underline;
261
+ }
262
+
263
+ .popup-link:first-child {
264
+ margin-top: 1em;
265
+ }
266
+
267
+ .popup-link:last-child {
268
+ margin-bottom: 1em;
269
+ }
270
+
238
271
  temba-dropdown > div[slot='dropdown'] .avatar > .details {
239
272
  margin-left: 0.75em;
240
273
  }
@@ -822,7 +855,7 @@ export class TembaMenu extends ResizeElement {
822
855
  }
823
856
 
824
857
  // eslint-disable-next-line @typescript-eslint/no-empty-function
825
- private loadItems(item: MenuItem, event: MouseEvent = null) {
858
+ private loadItems(item: MenuItem, event: MouseEvent | KeyboardEvent = null) {
826
859
  if (item && item.endpoint) {
827
860
  item.loading = true;
828
861
  this.httpComplete = fetchResults(item.endpoint)
@@ -872,7 +905,7 @@ export class TembaMenu extends ResizeElement {
872
905
  }
873
906
 
874
907
  private handleItemClicked(
875
- event: MouseEvent,
908
+ event: MouseEvent | KeyboardEvent,
876
909
  menuItem: MenuItem,
877
910
  parent: MenuItem = null
878
911
  ) {
@@ -895,16 +928,16 @@ export class TembaMenu extends ResizeElement {
895
928
  }
896
929
 
897
930
  private executeNavigation(
898
- event: MouseEvent,
931
+ event: MouseEvent | KeyboardEvent,
899
932
  menuItem: MenuItem,
900
933
  parent: MenuItem = null
901
934
  ) {
902
935
  if (parent && parent.popup) {
903
936
  const dropdown = this.shadowRoot.querySelector(
904
- 'temba-dropdown'
937
+ `#dd-${CSS.escape(parent.id)}`
905
938
  ) as Dropdown;
906
939
  if (dropdown) {
907
- dropdown.blur();
940
+ dropdown.close();
908
941
  }
909
942
 
910
943
  if (event) {
@@ -1148,6 +1181,32 @@ export class TembaMenu extends ResizeElement {
1148
1181
  return html`<div class="space"></div>`;
1149
1182
  }
1150
1183
 
1184
+ if (menuItem.type === 'link') {
1185
+ return html`<a
1186
+ id="menu-${menuItem.id}"
1187
+ class="popup-link"
1188
+ href=${ifDefined(menuItem.href ? menuItem.href : undefined)}
1189
+ target=${ifDefined(menuItem.target ? menuItem.target : undefined)}
1190
+ tabindex=${ifDefined(menuItem.href ? undefined : '0')}
1191
+ @click=${(event: MouseEvent) => {
1192
+ event.preventDefault();
1193
+ this.handleItemClicked(event, menuItem, parent);
1194
+ }}
1195
+ @keydown=${(event: KeyboardEvent) => {
1196
+ // anchors without an href aren't keyboard activatable on their own
1197
+ if (event.key === 'Enter' || event.key === ' ') {
1198
+ event.preventDefault();
1199
+ this.handleItemClicked(event, menuItem, parent);
1200
+ }
1201
+ }}
1202
+ >
1203
+ ${menuItem.icon
1204
+ ? html`<temba-icon name="${menuItem.icon}"></temba-icon>`
1205
+ : null}
1206
+ ${menuItem.name}
1207
+ </a>`;
1208
+ }
1209
+
1151
1210
  if (menuItem.type === 'section' || menuItem.inline) {
1152
1211
  return html`<div class="sub-section">${menuItem.name}</div>`;
1153
1212
  }
@@ -1240,7 +1299,7 @@ export class TembaMenu extends ResizeElement {
1240
1299
  }}
1241
1300
  >
1242
1301
  ${menuItem.level === 0
1243
- ? menuItem.avatar
1302
+ ? menuItem.avatar || menuItem.popup
1244
1303
  ? icon
1245
1304
  : html`<temba-tip
1246
1305
  position="right"
@@ -1286,6 +1345,8 @@ export class TembaMenu extends ResizeElement {
1286
1345
  return html`
1287
1346
  <temba-dropdown
1288
1347
  id="dd-${menuItem.id}"
1348
+ ?up=${!!menuItem.bottom}
1349
+ anchor="a"
1289
1350
  @temba-opened=${(event: CustomEvent) =>
1290
1351
  this.handlePopupOpened(event, menuItem)}
1291
1352
  >