@nyaruka/temba-components 0.96.0 → 0.98.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 (47) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/temba-components.js +169 -147
  3. package/dist/temba-components.js.map +1 -1
  4. package/out-tsc/src/contacts/ContactFieldEditor.js +139 -62
  5. package/out-tsc/src/contacts/ContactFieldEditor.js.map +1 -1
  6. package/out-tsc/src/contacts/ContactFields.js +2 -5
  7. package/out-tsc/src/contacts/ContactFields.js.map +1 -1
  8. package/out-tsc/src/options/Options.js +1 -0
  9. package/out-tsc/src/options/Options.js.map +1 -1
  10. package/out-tsc/src/select/Select.js +69 -68
  11. package/out-tsc/src/select/Select.js.map +1 -1
  12. package/out-tsc/src/templates/TemplateEditor.js +2 -10
  13. package/out-tsc/src/templates/TemplateEditor.js.map +1 -1
  14. package/out-tsc/test/temba-template-editor.test.js +2 -18
  15. package/out-tsc/test/temba-template-editor.test.js.map +1 -1
  16. package/package.json +1 -1
  17. package/screenshots/truth/select/disabled-multi-selection.png +0 -0
  18. package/screenshots/truth/select/disabled-selection.png +0 -0
  19. package/screenshots/truth/select/disabled.png +0 -0
  20. package/screenshots/truth/select/embedded.png +0 -0
  21. package/screenshots/truth/select/expression-selected.png +0 -0
  22. package/screenshots/truth/select/expressions.png +0 -0
  23. package/screenshots/truth/select/functions.png +0 -0
  24. package/screenshots/truth/select/local-options.png +0 -0
  25. package/screenshots/truth/select/remote-options.png +0 -0
  26. package/screenshots/truth/select/search-enabled.png +0 -0
  27. package/screenshots/truth/select/search-multi-no-matches.png +0 -0
  28. package/screenshots/truth/select/search-selected-focus.png +0 -0
  29. package/screenshots/truth/select/search-selected.png +0 -0
  30. package/screenshots/truth/select/search-with-selected.png +0 -0
  31. package/screenshots/truth/select/searching.png +0 -0
  32. package/screenshots/truth/select/selected-multi.png +0 -0
  33. package/screenshots/truth/select/selected-single.png +0 -0
  34. package/screenshots/truth/select/selection-clearable.png +0 -0
  35. package/screenshots/truth/select/truncated-selection.png +0 -0
  36. package/screenshots/truth/select/with-placeholder.png +0 -0
  37. package/screenshots/truth/select/without-placeholder.png +0 -0
  38. package/screenshots/truth/templates/default.png +0 -0
  39. package/screenshots/truth/templates/unapproved.png +0 -0
  40. package/src/contacts/ContactFieldEditor.ts +150 -62
  41. package/src/contacts/ContactFields.ts +2 -5
  42. package/src/options/Options.ts +1 -0
  43. package/src/select/Select.ts +65 -72
  44. package/src/templates/TemplateEditor.ts +3 -16
  45. package/static/api/templates.json +116 -201
  46. package/test/temba-template-editor.test.ts +2 -22
  47. package/screenshots/truth/templates/french.png +0 -0
@@ -20,7 +20,7 @@ import {
20
20
  executeCompletionQuery
21
21
  } from '../completion/helpers';
22
22
  import { Store } from '../store/Store';
23
- import { styleMap } from 'lit-html/directives/style-map.js';
23
+ import { StyleInfo, styleMap } from 'lit-html/directives/style-map.js';
24
24
  import { Icon } from '../vectoricon';
25
25
  import { msg } from '@lit/localize';
26
26
 
@@ -99,6 +99,7 @@ export class Select extends FormElement {
99
99
  padding-top: 1px;
100
100
  box-shadow: var(--widget-box-shadow);
101
101
  position: relative;
102
+ min-height: 2.5em;
102
103
  }
103
104
 
104
105
  temba-icon.select-open:hover,
@@ -204,57 +205,41 @@ export class Select extends FormElement {
204
205
  box-shadow: none !important;
205
206
  font-family: var(--font-family);
206
207
  caret-color: var(--input-caret);
208
+ border: 0px solid purple !important;
207
209
  }
208
210
 
209
- input:focus {
210
- box-shadow: none !important;
211
- }
212
-
213
- .searchable.no-search-input .input-wrapper {
214
- flex-grow: inherit;
211
+ .input-wrapper {
215
212
  min-width: 1px;
216
213
  }
217
214
 
218
- .searchable.no-search-input.empty .input-wrapper {
219
- flex-grow: 1;
215
+ .input-wrapper:focus-within {
220
216
  min-width: 1px;
221
217
  }
222
218
 
223
- .searchable.no-search-input .input-wrapper .searchbox {
224
- flex-grow: inherit;
225
- min-width: 1px;
219
+ .input-wrapper {
220
+ margin-left: 6px;
226
221
  }
227
222
 
228
- .searchable .input-wrapper .searchbox {
229
- flex-grow: 1;
230
- min-width: 80%;
231
- height: 100%;
223
+ .multi .input-wrapper {
224
+ margin-left: 2px !important;
232
225
  }
233
226
 
234
- .searchable.single.search-input .selected .selected-item {
227
+ .input-wrapper:focus-within .placeholder {
235
228
  display: none;
236
229
  }
237
230
 
238
- .searchable.single.no-search-input
239
- .selected
240
- .input-wrapper
241
- input.searchbox {
242
- padding: 6px 2px !important;
243
- }
244
-
245
- .searchable.single.no-search-input.empty
246
- .selected
247
- .input-wrapper
248
- input.searchbox {
249
- padding: 6px 6px !important;
231
+ input:focus {
232
+ box-shadow: none !important;
233
+ flex-grow: 1;
250
234
  }
251
235
 
252
- .empty input {
253
- width: 100%;
236
+ .searchable.search-input .input-wrapper {
237
+ flex-grow: 1;
238
+ min-width: 1px !important;
254
239
  }
255
240
 
256
- .searchable input {
257
- padding: 6px 4px !important;
241
+ .searchable.single.search-input .selected .selected-item {
242
+ display: none;
258
243
  }
259
244
 
260
245
  .searchable input {
@@ -264,7 +249,6 @@ export class Select extends FormElement {
264
249
  color: var(--color-text);
265
250
  resize: none;
266
251
  box-shadow: none !important;
267
- flex-grow: 1;
268
252
  border: none;
269
253
  caret-color: var(--input-caret);
270
254
  }
@@ -274,7 +258,8 @@ export class Select extends FormElement {
274
258
  }
275
259
 
276
260
  .input-wrapper {
277
- flex-grow: 1;
261
+ display: flex;
262
+ margin-right: 0em;
278
263
  }
279
264
 
280
265
  .input-wrapper .searchbox {
@@ -284,13 +269,6 @@ export class Select extends FormElement {
284
269
  border: 0px;
285
270
  }
286
271
 
287
- .searchbox::placeholder {
288
- color: var(--color-placeholder);
289
- font-size: 1em;
290
- line-height: var(--temba-select-selected-line-height);
291
- padding-left: 1px;
292
- }
293
-
294
272
  .placeholder {
295
273
  font-size: var(--temba-select-selected-font-size);
296
274
  color: var(--color-placeholder);
@@ -340,6 +318,9 @@ export class Select extends FormElement {
340
318
  `;
341
319
  }
342
320
 
321
+ @property({ type: Object })
322
+ inputStyle: StyleInfo = {};
323
+
343
324
  @property({ type: Boolean })
344
325
  multi = false;
345
326
 
@@ -643,7 +624,11 @@ export class Select extends FormElement {
643
624
  !this.fetching
644
625
  ) {
645
626
  if (this.isPastFetchThreshold()) {
646
- this.fetchOptions(this.query, this.page + 1);
627
+ if (this.next) {
628
+ this.fetchOptions(null, null, this.next);
629
+ } else {
630
+ this.fetchOptions(this.query, this.page + 1);
631
+ }
647
632
  }
648
633
  }
649
634
 
@@ -909,9 +894,9 @@ export class Select extends FormElement {
909
894
  }
910
895
  }
911
896
 
912
- public fetchOptions(query: string, page = 0) {
897
+ // TODO: do we still need to support page numbers?
898
+ public fetchOptions(query: string, page = 0, next = null) {
913
899
  this.completionOptions = [];
914
-
915
900
  if (!this.fetching) {
916
901
  this.fetching = true;
917
902
 
@@ -936,28 +921,27 @@ export class Select extends FormElement {
936
921
 
937
922
  if (this.endpoint) {
938
923
  let url = this.endpoint;
924
+ if (next) {
925
+ url = next;
926
+ } else {
927
+ if (query && this.queryParam) {
928
+ if (url.indexOf('?') > -1) {
929
+ url += '&';
930
+ } else {
931
+ url += '?';
932
+ }
939
933
 
940
- if (query && this.queryParam) {
941
- if (url.indexOf('?') > -1) {
942
- url += '&';
943
- } else {
944
- url += '?';
934
+ url += this.queryParam + '=' + encodeURIComponent(query);
945
935
  }
946
936
 
947
- url += this.queryParam + '=' + encodeURIComponent(query);
948
- }
949
-
950
- if (page) {
951
- if (url.indexOf('?') > -1) {
952
- url += '&';
953
- } else {
954
- url += '?';
937
+ if (page) {
938
+ if (url.indexOf('?') > -1) {
939
+ url += '&';
940
+ } else {
941
+ url += '?';
942
+ }
943
+ url += 'page=' + page;
955
944
  }
956
- url += 'page=' + page;
957
- }
958
-
959
- if (this.next) {
960
- url = this.next;
961
945
  }
962
946
 
963
947
  const cache = this.lruCache.get(url);
@@ -1000,6 +984,7 @@ export class Select extends FormElement {
1000
984
  }
1001
985
  );
1002
986
 
987
+ this.next = null;
1003
988
  const json = response.json;
1004
989
  if (json['next']) {
1005
990
  this.next = json['next'];
@@ -1025,9 +1010,7 @@ export class Select extends FormElement {
1025
1010
  });
1026
1011
  }
1027
1012
 
1028
- // if (!this.next) {
1029
1013
  this.fetching = false;
1030
- //}
1031
1014
  this.page = page;
1032
1015
  })
1033
1016
  .catch((reason: any) => {
@@ -1163,8 +1146,9 @@ export class Select extends FormElement {
1163
1146
  }
1164
1147
 
1165
1148
  private handleContainerClick(event: MouseEvent) {
1149
+ event.stopPropagation();
1150
+ event.preventDefault();
1166
1151
  this.focused = true;
1167
-
1168
1152
  if ((event.target as any).tagName !== 'INPUT') {
1169
1153
  const input = this.shadowRoot.querySelector('input');
1170
1154
  if (input) {
@@ -1175,8 +1159,6 @@ export class Select extends FormElement {
1175
1159
 
1176
1160
  if (this.visibleOptions.length > 0) {
1177
1161
  this.visibleOptions = [];
1178
- event.preventDefault();
1179
- event.stopPropagation();
1180
1162
  } else {
1181
1163
  this.requestUpdate('input');
1182
1164
  }
@@ -1196,10 +1178,12 @@ export class Select extends FormElement {
1196
1178
  }
1197
1179
 
1198
1180
  private handleArrowClick(event: MouseEvent): void {
1181
+ event.preventDefault();
1182
+ event.stopPropagation();
1199
1183
  if (this.visibleOptions.length > 0) {
1200
1184
  this.visibleOptions = [];
1201
- event.preventDefault();
1202
- event.stopPropagation();
1185
+ } else {
1186
+ this.handleContainerClick(event);
1203
1187
  }
1204
1188
  }
1205
1189
 
@@ -1244,6 +1228,10 @@ export class Select extends FormElement {
1244
1228
  evt.preventDefault();
1245
1229
  evt.stopPropagation();
1246
1230
  this.setValues([]);
1231
+ if (this.visibleOptions.length > 0) {
1232
+ this.visibleOptions = [];
1233
+ this.requestUpdate();
1234
+ }
1247
1235
  }
1248
1236
 
1249
1237
  public setValues(values: any[]) {
@@ -1315,19 +1303,21 @@ export class Select extends FormElement {
1315
1303
  <div class="input-wrapper">
1316
1304
  <input
1317
1305
  class="searchbox"
1306
+ style=${this.inputStyle ? styleMap(this.inputStyle) : ''}
1318
1307
  @input=${this.handleInput}
1319
1308
  @keydown=${this.handleKeyDown}
1320
1309
  @click=${this.handleClick}
1321
1310
  type="text"
1322
- placeholder=${placeholder}
1323
1311
  .value=${this.input}
1324
1312
  />
1325
1313
  <div id="anchor" style=${styleMap(anchorStyles)}></div>
1314
+ ${placeholderDiv}
1326
1315
  </div>
1327
1316
  `
1328
1317
  : placeholderDiv;
1329
1318
 
1330
1319
  return html`
1320
+
1331
1321
  <temba-field
1332
1322
  name=${this.name}
1333
1323
  .label=${this.label}
@@ -1339,12 +1329,15 @@ export class Select extends FormElement {
1339
1329
  >
1340
1330
  <slot></slot>
1341
1331
  <div class="wrapper-bg">
1332
+
1342
1333
  <div
1343
1334
  tabindex="0"
1344
1335
  class="select-container ${classes}"
1345
1336
  @click=${this.handleContainerClick}
1346
- >
1337
+ >
1338
+
1347
1339
  <div class="left-side">
1340
+ <slot name="prefix"></slot>
1348
1341
  <div class="selected">
1349
1342
  ${!this.multi ? input : null}
1350
1343
  ${this.values.map(
@@ -24,8 +24,8 @@ interface Template {
24
24
  created_on: string;
25
25
  modified_on: string;
26
26
  name: string;
27
- translations: Translation[];
28
27
  uuid: string;
28
+ base_translation: Translation;
29
29
  }
30
30
 
31
31
  export class TemplateEditor extends FormElement {
@@ -154,9 +154,6 @@ export class TemplateEditor extends FormElement {
154
154
  @property({ type: Object })
155
155
  selectedTemplate: Template;
156
156
 
157
- @property({ type: String })
158
- lang = 'eng-US';
159
-
160
157
  // initial variables, not reflected back
161
158
  @property({ type: Array })
162
159
  variables: string[];
@@ -192,22 +189,12 @@ export class TemplateEditor extends FormElement {
192
189
  private handleTemplateChanged(event: CustomEvent) {
193
190
  const prev = this.selectedTemplate;
194
191
  this.selectedTemplate = (event.target as any).values[0] as Template;
195
-
196
192
  if (prev) {
197
193
  this.currentVariables = [];
198
194
  }
199
195
 
200
- const [lang, loc] = this.lang.split('-');
201
196
  if (this.selectedTemplate) {
202
- this.translation = this.selectedTemplate.translations.find(
203
- (translation) => {
204
- return (
205
- translation.locale === this.lang ||
206
- (!loc && translation.locale.split('-')[0] === lang)
207
- );
208
- }
209
- );
210
-
197
+ this.translation = this.selectedTemplate.base_translation;
211
198
  if (this.translation) {
212
199
  this.variables = new Array(
213
200
  (this.translation.variables || []).length
@@ -405,7 +392,7 @@ export class TemplateEditor extends FormElement {
405
392
  content = this.renderComponents(this.translation.components);
406
393
  } else if (this.selectedTemplate) {
407
394
  content = html`<div class="error-message">
408
- No approved translation was found for current language.
395
+ This template currently has no approved translations.
409
396
  </div>`;
410
397
  }
411
398