@nyaruka/temba-components 0.122.0 → 0.123.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/.github/copilot-instructions.md +163 -0
- package/.github/workflows/build.yml +3 -3
- package/.github/workflows/cla.yml +6 -6
- package/.github/workflows/copilot-setup-steps.yml +86 -0
- package/CHANGELOG.md +33 -0
- package/demo/index.html +42 -0
- package/dist/locales/es.js +1 -0
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/fr.js +1 -0
- package/dist/locales/fr.js.map +1 -1
- package/dist/locales/pt.js +1 -0
- package/dist/locales/pt.js.map +1 -1
- package/dist/temba-components.js +59 -72
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/chart/TembaChart.js +81 -14
- package/out-tsc/src/chart/TembaChart.js.map +1 -1
- package/out-tsc/src/list/RunList.js +13 -8
- package/out-tsc/src/list/RunList.js.map +1 -1
- package/out-tsc/src/locales/es.js +1 -0
- package/out-tsc/src/locales/es.js.map +1 -1
- package/out-tsc/src/locales/fr.js +1 -0
- package/out-tsc/src/locales/fr.js.map +1 -1
- package/out-tsc/src/locales/pt.js +1 -0
- package/out-tsc/src/locales/pt.js.map +1 -1
- package/out-tsc/src/options/Options.js +36 -13
- package/out-tsc/src/options/Options.js.map +1 -1
- package/out-tsc/src/select/Select.js +28 -5
- package/out-tsc/src/select/Select.js.map +1 -1
- package/out-tsc/src/store/AppState.js +3 -3
- package/out-tsc/src/store/AppState.js.map +1 -1
- package/out-tsc/src/utils/index.js +6 -1
- package/out-tsc/src/utils/index.js.map +1 -1
- package/out-tsc/src/vectoricon/VectorIcon.js +2 -1
- package/out-tsc/src/vectoricon/VectorIcon.js.map +1 -1
- package/out-tsc/temba-modules.js +0 -2
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/temba-appstate-language.test.js +176 -0
- package/out-tsc/test/temba-appstate-language.test.js.map +1 -0
- package/out-tsc/test/temba-chart.test.js +125 -0
- package/out-tsc/test/temba-chart.test.js.map +1 -1
- package/out-tsc/test/temba-dropdown.test.js +317 -0
- package/out-tsc/test/temba-dropdown.test.js.map +1 -0
- package/out-tsc/test/temba-run-list.test.js +588 -0
- package/out-tsc/test/temba-run-list.test.js.map +1 -0
- package/out-tsc/test/temba-select.test.js +16 -0
- package/out-tsc/test/temba-select.test.js.map +1 -1
- package/out-tsc/test/temba-toast.test.js +299 -0
- package/out-tsc/test/temba-toast.test.js.map +1 -0
- package/out-tsc/test/temba-utils-index.test.js +1178 -0
- package/out-tsc/test/temba-utils-index.test.js.map +1 -0
- package/out-tsc/test/temba-webchat.test.js +816 -0
- package/out-tsc/test/temba-webchat.test.js.map +1 -0
- package/out-tsc/test/utils.test.js +3 -1
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +6 -8
- package/screenshots/truth/alert/error.png +0 -0
- package/screenshots/truth/alert/info.png +0 -0
- package/screenshots/truth/alert/warning.png +0 -0
- package/screenshots/truth/checkbox/checkbox-label-background-hover.png +0 -0
- package/screenshots/truth/checkbox/checked.png +0 -0
- package/screenshots/truth/checkbox/default.png +0 -0
- package/screenshots/truth/colorpicker/default.png +0 -0
- package/screenshots/truth/colorpicker/focused.png +0 -0
- package/screenshots/truth/colorpicker/initialized.png +0 -0
- package/screenshots/truth/colorpicker/selected.png +0 -0
- package/screenshots/truth/compose/attachments-tab.png +0 -0
- package/screenshots/truth/compose/attachments-with-files-focused.png +0 -0
- package/screenshots/truth/compose/attachments-with-files.png +0 -0
- package/screenshots/truth/compose/intial-text.png +0 -0
- package/screenshots/truth/compose/no-counter.png +0 -0
- package/screenshots/truth/compose/wraps-text-and-spaces.png +0 -0
- package/screenshots/truth/compose/wraps-text-and-url.png +0 -0
- package/screenshots/truth/compose/wraps-text-no-spaces.png +0 -0
- package/screenshots/truth/contacts/badges.png +0 -0
- package/screenshots/truth/contacts/chat-failure.png +0 -0
- package/screenshots/truth/contacts/chat-for-active-contact.png +0 -0
- package/screenshots/truth/contacts/chat-for-archived-contact.png +0 -0
- package/screenshots/truth/contacts/chat-for-blocked-contact.png +0 -0
- package/screenshots/truth/contacts/chat-for-stopped-contact.png +0 -0
- package/screenshots/truth/contacts/chat-sends-attachments-only.png +0 -0
- package/screenshots/truth/contacts/chat-sends-text-and-attachments.png +0 -0
- package/screenshots/truth/contacts/chat-sends-text-only.png +0 -0
- package/screenshots/truth/content-menu/button-no-items.png +0 -0
- package/screenshots/truth/content-menu/items-and-buttons.png +0 -0
- package/screenshots/truth/counter/summary.png +0 -0
- package/screenshots/truth/counter/text.png +0 -0
- package/screenshots/truth/counter/unicode-variables.png +0 -0
- package/screenshots/truth/counter/unicode.png +0 -0
- package/screenshots/truth/counter/variable.png +0 -0
- package/screenshots/truth/date/date-inline.png +0 -0
- package/screenshots/truth/date/date.png +0 -0
- package/screenshots/truth/date/datetime.png +0 -0
- package/screenshots/truth/date/duration.png +0 -0
- package/screenshots/truth/date/timedate.png +0 -0
- package/screenshots/truth/datepicker/date-truncated-time.png +0 -0
- package/screenshots/truth/datepicker/date.png +0 -0
- package/screenshots/truth/datepicker/initial-timezone.png +0 -0
- package/screenshots/truth/datepicker/updated-keyboard-date.png +0 -0
- package/screenshots/truth/dialog/focused.png +0 -0
- package/screenshots/truth/dropdown/after-blur.png +0 -0
- package/screenshots/truth/dropdown/bottom-edge-collision.png +0 -0
- package/screenshots/truth/dropdown/custom-arrow-size.png +0 -0
- package/screenshots/truth/dropdown/default.png +0 -0
- package/screenshots/truth/dropdown/narrow-toggle.png +0 -0
- package/screenshots/truth/dropdown/no-mask.png +0 -0
- package/screenshots/truth/dropdown/opened.png +0 -0
- package/screenshots/truth/dropdown/positioned.png +0 -0
- package/screenshots/truth/dropdown/right-edge-collision.png +0 -0
- package/screenshots/truth/dropdown/with-mask.png +0 -0
- package/screenshots/truth/label/custom.png +0 -0
- package/screenshots/truth/label/danger.png +0 -0
- package/screenshots/truth/label/dark.png +0 -0
- package/screenshots/truth/label/default-icon.png +0 -0
- package/screenshots/truth/label/no-icon.png +0 -0
- package/screenshots/truth/label/primary.png +0 -0
- package/screenshots/truth/label/secondary.png +0 -0
- package/screenshots/truth/label/shadow.png +0 -0
- package/screenshots/truth/label/tertiary.png +0 -0
- package/screenshots/truth/lightbox/img-zoomed.png +0 -0
- package/screenshots/truth/list/fields-dragging.png +0 -0
- package/screenshots/truth/list/fields-filtered.png +0 -0
- package/screenshots/truth/list/fields-hovered.png +0 -0
- package/screenshots/truth/list/fields.png +0 -0
- package/screenshots/truth/list/items-selected.png +0 -0
- package/screenshots/truth/list/items-updated.png +0 -0
- package/screenshots/truth/list/items.png +0 -0
- package/screenshots/truth/list/sortable-dragging.png +0 -0
- package/screenshots/truth/list/sortable-dropped.png +0 -0
- package/screenshots/truth/list/sortable.png +0 -0
- package/screenshots/truth/menu/menu-focused-with items.png +0 -0
- package/screenshots/truth/menu/menu-refresh-1.png +0 -0
- package/screenshots/truth/menu/menu-refresh-2.png +0 -0
- package/screenshots/truth/menu/menu-root.png +0 -0
- package/screenshots/truth/menu/menu-submenu.png +0 -0
- package/screenshots/truth/menu/menu-tasks-nextup.png +0 -0
- package/screenshots/truth/menu/menu-tasks.png +0 -0
- package/screenshots/truth/modax/form.png +0 -0
- package/screenshots/truth/modax/simple.png +0 -0
- package/screenshots/truth/omnibox/selected.png +0 -0
- package/screenshots/truth/options/block.png +0 -0
- package/screenshots/truth/run-list/basic.png +0 -0
- package/screenshots/truth/select/disabled-multi-selection.png +0 -0
- package/screenshots/truth/select/disabled-selection.png +0 -0
- package/screenshots/truth/select/disabled.png +0 -0
- package/screenshots/truth/select/embedded.png +0 -0
- package/screenshots/truth/select/empty-options.png +0 -0
- package/screenshots/truth/select/expression-selected.png +0 -0
- package/screenshots/truth/select/expressions.png +0 -0
- package/screenshots/truth/select/functions.png +0 -0
- package/screenshots/truth/select/local-options.png +0 -0
- package/screenshots/truth/select/multi-with-endpoint.png +0 -0
- package/screenshots/truth/select/multiple-initial-values.png +0 -0
- package/screenshots/truth/select/remote-options.png +0 -0
- package/screenshots/truth/select/search-enabled.png +0 -0
- package/screenshots/truth/select/search-multi-no-matches.png +0 -0
- package/screenshots/truth/select/search-selected-focus.png +0 -0
- package/screenshots/truth/select/search-selected.png +0 -0
- package/screenshots/truth/select/search-with-selected.png +0 -0
- package/screenshots/truth/select/searching.png +0 -0
- package/screenshots/truth/select/selected-multi-maxitems-reached.png +0 -0
- package/screenshots/truth/select/selected-multi.png +0 -0
- package/screenshots/truth/select/selected-single.png +0 -0
- package/screenshots/truth/select/selection-clearable.png +0 -0
- package/screenshots/truth/select/static-initial-value.png +0 -0
- package/screenshots/truth/select/static-initial-via-selected.png +0 -0
- package/screenshots/truth/select/truncated-selection.png +0 -0
- package/screenshots/truth/select/with-placeholder.png +0 -0
- package/screenshots/truth/select/without-placeholder.png +0 -0
- package/screenshots/truth/slider/custom-min-custom-max-valid-value.png +0 -0
- package/screenshots/truth/slider/custom-min-default-max-no-value.png +0 -0
- package/screenshots/truth/slider/default-min-custom-max-no-value.png +0 -0
- package/screenshots/truth/slider/default-min-default-max-invalid-value.png +0 -0
- package/screenshots/truth/slider/default-min-default-max-valid-value.png +0 -0
- package/screenshots/truth/slider/update-slider-on-value-change.png +0 -0
- package/screenshots/truth/templates/default.png +0 -0
- package/screenshots/truth/templates/unapproved.png +0 -0
- package/screenshots/truth/textinput/input-disabled.png +0 -0
- package/screenshots/truth/textinput/input-focused.png +0 -0
- package/screenshots/truth/textinput/input-form.png +0 -0
- package/screenshots/truth/textinput/input-inserted.png +0 -0
- package/screenshots/truth/textinput/input-placeholder.png +0 -0
- package/screenshots/truth/textinput/input-updated.png +0 -0
- package/screenshots/truth/textinput/input.png +0 -0
- package/screenshots/truth/textinput/textarea-focused.png +0 -0
- package/screenshots/truth/textinput/textarea.png +0 -0
- package/screenshots/truth/tip/bottom.png +0 -0
- package/screenshots/truth/tip/left.png +0 -0
- package/screenshots/truth/tip/right.png +0 -0
- package/screenshots/truth/tip/top.png +0 -0
- package/screenshots/truth/webchat/closed-widget.png +0 -0
- package/screenshots/truth/webchat/connected-state.png +0 -0
- package/screenshots/truth/webchat/connecting-state.png +0 -0
- package/screenshots/truth/webchat/disconnected-state.png +0 -0
- package/screenshots/truth/webchat/opened-widget.png +0 -0
- package/src/chart/TembaChart.ts +86 -15
- package/src/list/RunList.ts +11 -8
- package/src/locales/es.ts +1 -0
- package/src/locales/fr.ts +1 -0
- package/src/locales/pt.ts +1 -0
- package/src/options/Options.ts +38 -13
- package/src/select/Select.ts +32 -5
- package/src/store/AppState.ts +3 -3
- package/src/utils/index.ts +17 -5
- package/src/vectoricon/VectorIcon.ts +2 -1
- package/temba-modules.ts +0 -2
- package/test/temba-appstate-language.test.ts +218 -0
- package/test/temba-chart.test.ts +161 -1
- package/test/temba-dropdown.test.ts +444 -0
- package/test/temba-run-list.test.ts +774 -0
- package/test/temba-select.test.ts +27 -0
- package/test/temba-toast.test.ts +386 -0
- package/test/temba-utils-index.test.ts +1547 -0
- package/test/temba-webchat.test.ts +1095 -0
- package/test/utils.test.ts +4 -2
- package/test-assets/list/flow-results.json +17 -0
- package/test-assets/list/runs.json +126 -0
- package/test-assets/style.css +23 -0
- package/web-test-runner.config.mjs +33 -7
- package/xliff/es.xlf +3 -0
- package/xliff/fr.xlf +3 -0
- package/xliff/pt.xlf +3 -0
- package/out-tsc/src/outboxmonitor/OutboxMonitor.js +0 -136
- package/out-tsc/src/outboxmonitor/OutboxMonitor.js.map +0 -1
- package/src/outboxmonitor/OutboxMonitor.ts +0 -148
|
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/src/chart/TembaChart.ts
CHANGED
|
@@ -6,35 +6,66 @@ import { Select, SelectOption } from '../select/Select';
|
|
|
6
6
|
import { getClasses } from '../utils';
|
|
7
7
|
import { getStore } from '../store/Store';
|
|
8
8
|
|
|
9
|
+
// eslint-disable-next-line import/no-named-as-default
|
|
9
10
|
import Chart, { ChartType } from 'chart.js/auto';
|
|
10
11
|
import 'chartjs-adapter-luxon';
|
|
11
12
|
|
|
12
13
|
const colors = [
|
|
14
|
+
'rgba(54, 162, 235, 0.2)',
|
|
13
15
|
'rgba(255, 159, 64, 0.2)',
|
|
14
16
|
'rgba(75, 192, 192, 0.2)',
|
|
15
|
-
'rgba(54, 162, 235, 0.2)',
|
|
16
17
|
'rgba(153, 102, 255, 0.2)',
|
|
17
18
|
'rgba(255, 205, 86, 0.2)',
|
|
18
|
-
'rgba(255, 99, 132, 0.2)'
|
|
19
|
-
'rgba(201, 203, 207, 0.2)'
|
|
19
|
+
'rgba(255, 99, 132, 0.2)'
|
|
20
20
|
];
|
|
21
21
|
|
|
22
22
|
const colorsBorder = [
|
|
23
|
+
'rgb(54, 162, 235)',
|
|
23
24
|
'rgb(255, 159, 64)',
|
|
24
25
|
'rgb(75, 192, 192)',
|
|
25
|
-
'rgb(54, 162, 235)',
|
|
26
26
|
'rgb(153, 102, 255)',
|
|
27
27
|
'rgb(255, 205, 86)',
|
|
28
|
-
'rgb(255, 99, 132)'
|
|
29
|
-
'rgb(201, 203, 207)'
|
|
28
|
+
'rgb(255, 99, 132)'
|
|
30
29
|
];
|
|
31
30
|
|
|
32
|
-
const allBorderColor = 'rgb(54, 162, 235)';
|
|
33
|
-
const allBackgroundColor = 'rgba(54, 162, 235, 0.2)';
|
|
34
|
-
|
|
35
31
|
const otherBackgroundColor = 'rgba(201, 203, 207, 0.2)';
|
|
36
32
|
const otherBorderColor = 'rgb(201, 203, 207)';
|
|
37
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Formats a duration in seconds to a human-readable string showing the two largest units.
|
|
36
|
+
* Examples: 68787 -> "19h 6m", 958000 -> "11d 2h", 3661 -> "1h 1m"
|
|
37
|
+
*/
|
|
38
|
+
export function formatDurationFromSeconds(seconds: number): string {
|
|
39
|
+
if (seconds === 0) {
|
|
40
|
+
return '0s';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const totalDays = Math.floor(seconds / 86400);
|
|
44
|
+
const remainingAfterDays = seconds % 86400;
|
|
45
|
+
const remainingHours = Math.floor(remainingAfterDays / 3600);
|
|
46
|
+
const remainingAfterHours = remainingAfterDays % 3600;
|
|
47
|
+
const remainingMinutes = Math.floor(remainingAfterHours / 60);
|
|
48
|
+
const remainingSeconds = remainingAfterHours % 60;
|
|
49
|
+
|
|
50
|
+
const units = [];
|
|
51
|
+
|
|
52
|
+
if (totalDays > 0) {
|
|
53
|
+
units.push(`${totalDays}d`);
|
|
54
|
+
}
|
|
55
|
+
if (remainingHours > 0) {
|
|
56
|
+
units.push(`${remainingHours}h`);
|
|
57
|
+
}
|
|
58
|
+
if (remainingMinutes > 0 && units.length < 2) {
|
|
59
|
+
units.push(`${remainingMinutes}m`);
|
|
60
|
+
}
|
|
61
|
+
if (remainingSeconds > 0 && units.length < 2) {
|
|
62
|
+
units.push(`${remainingSeconds}s`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Return the first two most significant units
|
|
66
|
+
return units.slice(0, 2).join(' ');
|
|
67
|
+
}
|
|
68
|
+
|
|
38
69
|
export interface RapidChartData {
|
|
39
70
|
labels: string[];
|
|
40
71
|
datasets: { label: string; data: number[] }[];
|
|
@@ -68,9 +99,21 @@ export class TembaChart extends RapidElement {
|
|
|
68
99
|
@property({ type: String })
|
|
69
100
|
dataname = 'Counts';
|
|
70
101
|
|
|
102
|
+
@property({ type: Boolean })
|
|
103
|
+
single: boolean = false;
|
|
104
|
+
|
|
105
|
+
@property({ type: Boolean })
|
|
106
|
+
legend: boolean = false;
|
|
107
|
+
|
|
71
108
|
@property({ type: Boolean })
|
|
72
109
|
config: boolean = false;
|
|
73
110
|
|
|
111
|
+
@property({ type: Boolean })
|
|
112
|
+
formatDuration: boolean = false;
|
|
113
|
+
|
|
114
|
+
@property({ type: Number })
|
|
115
|
+
colorIndex: number = 0;
|
|
116
|
+
|
|
74
117
|
@state()
|
|
75
118
|
showConfig: boolean = false;
|
|
76
119
|
|
|
@@ -180,8 +223,12 @@ export class TembaChart extends RapidElement {
|
|
|
180
223
|
} else {
|
|
181
224
|
datasets.push({
|
|
182
225
|
...dataset,
|
|
183
|
-
backgroundColor:
|
|
184
|
-
|
|
226
|
+
backgroundColor:
|
|
227
|
+
colors[(datasets.length + this.colorIndex) % colors.length],
|
|
228
|
+
borderColor:
|
|
229
|
+
colorsBorder[
|
|
230
|
+
(datasets.length + this.colorIndex) % colorsBorder.length
|
|
231
|
+
],
|
|
185
232
|
borderWidth: 1
|
|
186
233
|
});
|
|
187
234
|
}
|
|
@@ -189,10 +236,10 @@ export class TembaChart extends RapidElement {
|
|
|
189
236
|
|
|
190
237
|
if (datasets.length === 0) {
|
|
191
238
|
datasets.push({
|
|
192
|
-
label: `All ${this.dataname}`,
|
|
239
|
+
label: this.single ? this.dataname : `All ${this.dataname}`,
|
|
193
240
|
data: sums,
|
|
194
|
-
backgroundColor:
|
|
195
|
-
borderColor:
|
|
241
|
+
backgroundColor: colors[this.colorIndex % colors.length],
|
|
242
|
+
borderColor: colorsBorder[this.colorIndex % colorsBorder.length],
|
|
196
243
|
borderWidth: 1
|
|
197
244
|
});
|
|
198
245
|
} else {
|
|
@@ -222,6 +269,23 @@ export class TembaChart extends RapidElement {
|
|
|
222
269
|
datasets: this.datasets
|
|
223
270
|
},
|
|
224
271
|
options: {
|
|
272
|
+
plugins: {
|
|
273
|
+
legend: {
|
|
274
|
+
display: this.legend
|
|
275
|
+
},
|
|
276
|
+
...(this.formatDuration && {
|
|
277
|
+
tooltip: {
|
|
278
|
+
callbacks: {
|
|
279
|
+
label: (context: any) => {
|
|
280
|
+
const label = context.dataset.label || '';
|
|
281
|
+
const value = context.parsed.y;
|
|
282
|
+
const formattedValue = formatDurationFromSeconds(value);
|
|
283
|
+
return `${label}: ${formattedValue}`;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
})
|
|
288
|
+
},
|
|
225
289
|
responsive: true,
|
|
226
290
|
maintainAspectRatio: false,
|
|
227
291
|
animation: {
|
|
@@ -240,7 +304,14 @@ export class TembaChart extends RapidElement {
|
|
|
240
304
|
scales: {
|
|
241
305
|
y: {
|
|
242
306
|
min: 0,
|
|
243
|
-
stacked: true
|
|
307
|
+
stacked: true,
|
|
308
|
+
...(this.formatDuration && {
|
|
309
|
+
ticks: {
|
|
310
|
+
callback: (value: any) => {
|
|
311
|
+
return formatDurationFromSeconds(value);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
})
|
|
244
315
|
},
|
|
245
316
|
x: {
|
|
246
317
|
type: 'time',
|
package/src/list/RunList.ts
CHANGED
|
@@ -155,8 +155,8 @@ export class RunList extends TembaList {
|
|
|
155
155
|
style="width:16em;white-space:nowrap;overflow: hidden; text-overflow: ellipsis;"
|
|
156
156
|
>
|
|
157
157
|
<temba-contact-name
|
|
158
|
-
name=${run.contact
|
|
159
|
-
urn=${run.contact
|
|
158
|
+
name=${run.contact?.name || run.contact?.anon_display || ''}
|
|
159
|
+
urn=${run.contact?.urn || ''}
|
|
160
160
|
icon-size="15"
|
|
161
161
|
/>
|
|
162
162
|
</div>
|
|
@@ -255,7 +255,7 @@ export class RunList extends TembaList {
|
|
|
255
255
|
return null;
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
const resultKeys = Object.keys(this.selectedRun.values);
|
|
258
|
+
const resultKeys = Object.keys(this.selectedRun.values || {});
|
|
259
259
|
|
|
260
260
|
return html` <div
|
|
261
261
|
style="margin-top: 1.5em; margin-bottom:0.5em;flex-grow:1;border-radius:var(--curvature); border: 1px solid var(--color-widget-border);"
|
|
@@ -267,11 +267,12 @@ export class RunList extends TembaList {
|
|
|
267
267
|
<div>
|
|
268
268
|
<temba-contact-name
|
|
269
269
|
style="cursor:pointer"
|
|
270
|
-
name=${this.selectedRun.contact
|
|
271
|
-
this.selectedRun.contact
|
|
272
|
-
|
|
270
|
+
name=${this.selectedRun.contact?.name ||
|
|
271
|
+
this.selectedRun.contact?.anon_display ||
|
|
272
|
+
''}
|
|
273
|
+
urn=${this.selectedRun.contact?.urn || ''}
|
|
273
274
|
onclick="goto(event, this)"
|
|
274
|
-
href="/contact/read/${this.selectedRun.contact
|
|
275
|
+
href="/contact/read/${this.selectedRun.contact?.uuid || ''}/"
|
|
275
276
|
></temba-contact-name>
|
|
276
277
|
<div
|
|
277
278
|
style="display:flex;margin-left:-0.2em;margin-top:0.25em;font-size: 0.65em"
|
|
@@ -303,7 +304,9 @@ export class RunList extends TembaList {
|
|
|
303
304
|
<div style="flex-grow:1"></div>
|
|
304
305
|
<div style="display:flex;flex-direction: column">
|
|
305
306
|
<div style="font-size:0.75em">
|
|
306
|
-
${
|
|
307
|
+
${this.selectedRun.created_on
|
|
308
|
+
? new Date(this.selectedRun.created_on).toLocaleString()
|
|
309
|
+
: ''}
|
|
307
310
|
</div>
|
|
308
311
|
<div
|
|
309
312
|
style="font-size:0.6em;align-self:flex-end;color:#888;line-height:0.75em"
|
package/src/locales/es.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
export const templates = {
|
|
8
8
|
scf1453991c986b25: `Tab para completar, enter para seleccionar`,
|
|
9
|
+
s73b4d70c02f4b4e0: `No options`,
|
|
9
10
|
s8f02e3a18ffc083a: `Are not currently in a flow`,
|
|
10
11
|
s638236250662c6b3: `Have sent a message in the last`,
|
|
11
12
|
s4788ee206c4570c7: `Have not started this flow in the last 90 days`
|
package/src/locales/fr.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
6
6
|
|
|
7
7
|
export const templates = {
|
|
8
|
+
s73b4d70c02f4b4e0: `No options`,
|
|
8
9
|
scf1453991c986b25: `Tab to complete, enter to select`,
|
|
9
10
|
s8f02e3a18ffc083a: `Are not currently in a flow`,
|
|
10
11
|
s638236250662c6b3: `Have sent a message in the last`,
|
package/src/locales/pt.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
6
6
|
|
|
7
7
|
export const templates = {
|
|
8
|
+
s73b4d70c02f4b4e0: `No options`,
|
|
8
9
|
scf1453991c986b25: `Tab to complete, enter to select`,
|
|
9
10
|
s8f02e3a18ffc083a: `Are not currently in a flow`,
|
|
10
11
|
s638236250662c6b3: `Have sent a message in the last`,
|
package/src/options/Options.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { CustomEventType } from '../interfaces';
|
|
|
4
4
|
import { RapidElement, EventHandler } from '../RapidElement';
|
|
5
5
|
import { styleMap } from 'lit-html/directives/style-map.js';
|
|
6
6
|
import { getClasses, getScrollParent, throttle } from '../utils';
|
|
7
|
+
import { msg } from '@lit/localize';
|
|
7
8
|
|
|
8
9
|
export class Options extends RapidElement {
|
|
9
10
|
static get styles() {
|
|
@@ -177,6 +178,15 @@ export class Options extends RapidElement {
|
|
|
177
178
|
background: var(--color-selection);
|
|
178
179
|
color: var(--color-text-dark);
|
|
179
180
|
}
|
|
181
|
+
|
|
182
|
+
.option.no-options {
|
|
183
|
+
pointer-events: none;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.option.no-options:hover {
|
|
187
|
+
background: transparent;
|
|
188
|
+
color: var(--color-text-dark-secondary);
|
|
189
|
+
}
|
|
180
190
|
`;
|
|
181
191
|
}
|
|
182
192
|
|
|
@@ -276,6 +286,9 @@ export class Options extends RapidElement {
|
|
|
276
286
|
@property({ type: Boolean })
|
|
277
287
|
triggerScroll = false;
|
|
278
288
|
|
|
289
|
+
@property({ type: Boolean })
|
|
290
|
+
showEmptyMessage = false;
|
|
291
|
+
|
|
279
292
|
scrollParent: HTMLElement = null;
|
|
280
293
|
|
|
281
294
|
resolvedRenderOption: { (option: any, selected: boolean): TemplateResult };
|
|
@@ -675,19 +688,31 @@ export class Options extends RapidElement {
|
|
|
675
688
|
<div class=${classes} style=${styleMap(containerStyle)}>
|
|
676
689
|
<div class="options-scroll" @scroll=${this.handleInnerScroll}>
|
|
677
690
|
<div class="${classesInner}" style=${styleMap(optionsStyle)}>
|
|
678
|
-
${options.
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
+
${options.length > 0
|
|
692
|
+
? options.map((option, index) => {
|
|
693
|
+
return html`<div
|
|
694
|
+
data-option-index="${index}"
|
|
695
|
+
@mousemove=${this.handleMouseMove}
|
|
696
|
+
@mousedown=${this.handleOptionClick}
|
|
697
|
+
class="option ${index === this.cursorIndex &&
|
|
698
|
+
!this.internalFocusDisabled
|
|
699
|
+
? 'focused'
|
|
700
|
+
: ''}"
|
|
701
|
+
>
|
|
702
|
+
${this.resolvedRenderOption(
|
|
703
|
+
option,
|
|
704
|
+
index === this.cursorIndex
|
|
705
|
+
)}
|
|
706
|
+
</div>`;
|
|
707
|
+
})
|
|
708
|
+
: this.visible && this.showEmptyMessage
|
|
709
|
+
? html`<div
|
|
710
|
+
class="option no-options"
|
|
711
|
+
style="color: var(--color-text-dark-secondary); cursor: default;"
|
|
712
|
+
>
|
|
713
|
+
${msg('No options')}
|
|
714
|
+
</div>`
|
|
715
|
+
: null}
|
|
691
716
|
${this.block ? html`<div style="height:0.1em"></div>` : null}
|
|
692
717
|
</div>
|
|
693
718
|
<slot></slot>
|
package/src/select/Select.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
|
2
2
|
import { TemplateResult, html, css, CSSResult, CSSResultArray } from 'lit';
|
|
3
|
-
import { property } from 'lit/decorators.js';
|
|
3
|
+
import { property, state } from 'lit/decorators.js';
|
|
4
4
|
import {
|
|
5
5
|
getUrl,
|
|
6
6
|
getClasses,
|
|
@@ -410,6 +410,9 @@ export class Select<T extends SelectOption> extends FormElement {
|
|
|
410
410
|
@property({ type: Boolean })
|
|
411
411
|
disabled = false;
|
|
412
412
|
|
|
413
|
+
@state()
|
|
414
|
+
attemptedOpen = false;
|
|
415
|
+
|
|
413
416
|
@property({ attribute: false })
|
|
414
417
|
selectedIndex = -1;
|
|
415
418
|
|
|
@@ -863,6 +866,7 @@ export class Select<T extends SelectOption> extends FormElement {
|
|
|
863
866
|
}
|
|
864
867
|
|
|
865
868
|
this.visibleOptions = [];
|
|
869
|
+
this.attemptedOpen = false;
|
|
866
870
|
this.input = '';
|
|
867
871
|
this.next = null;
|
|
868
872
|
this.complete = true;
|
|
@@ -955,7 +959,9 @@ export class Select<T extends SelectOption> extends FormElement {
|
|
|
955
959
|
}
|
|
956
960
|
|
|
957
961
|
public isOpen(): boolean {
|
|
958
|
-
return
|
|
962
|
+
return (
|
|
963
|
+
this.visibleOptions.length > 0 || (this.attemptedOpen && this.focused)
|
|
964
|
+
);
|
|
959
965
|
}
|
|
960
966
|
|
|
961
967
|
public setOptions(options: any[]): void {
|
|
@@ -1212,6 +1218,7 @@ export class Select<T extends SelectOption> extends FormElement {
|
|
|
1212
1218
|
|
|
1213
1219
|
private handleBlur() {
|
|
1214
1220
|
this.focused = false;
|
|
1221
|
+
this.attemptedOpen = false;
|
|
1215
1222
|
if (this.visibleOptions.length > 0) {
|
|
1216
1223
|
this.input = '';
|
|
1217
1224
|
this.next = null;
|
|
@@ -1285,8 +1292,10 @@ export class Select<T extends SelectOption> extends FormElement {
|
|
|
1285
1292
|
) {
|
|
1286
1293
|
if (
|
|
1287
1294
|
this.visibleOptions.length === 0 &&
|
|
1288
|
-
this.completionOptions.length === 0
|
|
1295
|
+
this.completionOptions.length === 0 &&
|
|
1296
|
+
!this.input
|
|
1289
1297
|
) {
|
|
1298
|
+
this.attemptedOpen = true;
|
|
1290
1299
|
this.requestUpdate('input');
|
|
1291
1300
|
return;
|
|
1292
1301
|
}
|
|
@@ -1322,6 +1331,7 @@ export class Select<T extends SelectOption> extends FormElement {
|
|
|
1322
1331
|
|
|
1323
1332
|
private handleCancel() {
|
|
1324
1333
|
this.visibleOptions = [];
|
|
1334
|
+
this.attemptedOpen = false;
|
|
1325
1335
|
}
|
|
1326
1336
|
|
|
1327
1337
|
private handleCursorChanged(event: CustomEvent) {
|
|
@@ -1340,10 +1350,15 @@ export class Select<T extends SelectOption> extends FormElement {
|
|
|
1340
1350
|
return;
|
|
1341
1351
|
}
|
|
1342
1352
|
|
|
1343
|
-
if (
|
|
1353
|
+
// Check if dropdown is currently open (either with options or showing "No options")
|
|
1354
|
+
if (this.isOpen()) {
|
|
1344
1355
|
this.visibleOptions = [];
|
|
1356
|
+
this.attemptedOpen = false;
|
|
1345
1357
|
} else {
|
|
1358
|
+
this.attemptedOpen = true;
|
|
1346
1359
|
this.requestUpdate('input');
|
|
1360
|
+
// Also trigger an immediate update to show empty dropdown
|
|
1361
|
+
this.requestUpdate();
|
|
1347
1362
|
}
|
|
1348
1363
|
}
|
|
1349
1364
|
}
|
|
@@ -1461,6 +1476,17 @@ export class Select<T extends SelectOption> extends FormElement {
|
|
|
1461
1476
|
this.requestUpdate('values', oldValues);
|
|
1462
1477
|
}
|
|
1463
1478
|
|
|
1479
|
+
private shouldShowEmptyMessage(): boolean {
|
|
1480
|
+
return (
|
|
1481
|
+
this.attemptedOpen &&
|
|
1482
|
+
this.focused &&
|
|
1483
|
+
this.visibleOptions.length === 0 &&
|
|
1484
|
+
!this.input &&
|
|
1485
|
+
this.staticOptions.length === 0 &&
|
|
1486
|
+
!this.endpoint
|
|
1487
|
+
);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1464
1490
|
public render(): TemplateResult {
|
|
1465
1491
|
const placeholder = this.values.length === 0 ? this.placeholder : '';
|
|
1466
1492
|
const placeholderDiv = html`
|
|
@@ -1620,7 +1646,8 @@ export class Select<T extends SelectOption> extends FormElement {
|
|
|
1620
1646
|
.getName=${this.getNameInternal}
|
|
1621
1647
|
?static-width=${this.optionWidth}
|
|
1622
1648
|
?anchor-right=${this.anchorRight}
|
|
1623
|
-
?visible=${this.visibleOptions.length > 0}
|
|
1649
|
+
?visible=${this.visibleOptions.length > 0 || this.shouldShowEmptyMessage()}
|
|
1650
|
+
?showEmptyMessage=${this.shouldShowEmptyMessage()}
|
|
1624
1651
|
></temba-options>
|
|
1625
1652
|
|
|
1626
1653
|
<temba-options
|
package/src/store/AppState.ts
CHANGED
|
@@ -157,11 +157,11 @@ export const zustand = createStore<AppState>()(
|
|
|
157
157
|
setFlowContents: (flow: FlowContents) => {
|
|
158
158
|
set((state: AppState) => {
|
|
159
159
|
const flowLang = flow.definition.language;
|
|
160
|
-
const languageCode = state.languageCode || flowLang;
|
|
161
160
|
state.flowDefinition = flow.definition;
|
|
162
161
|
state.flowInfo = flow.info;
|
|
163
|
-
|
|
164
|
-
state.languageCode =
|
|
162
|
+
// Reset to the flow's default language when loading a new flow
|
|
163
|
+
state.languageCode = flowLang;
|
|
164
|
+
state.isTranslating = false;
|
|
165
165
|
});
|
|
166
166
|
},
|
|
167
167
|
|
package/src/utils/index.ts
CHANGED
|
@@ -494,7 +494,7 @@ export const getScrollParent = (node: any): any => {
|
|
|
494
494
|
return null;
|
|
495
495
|
};
|
|
496
496
|
|
|
497
|
-
export const isElementVisible = (el: any, holder
|
|
497
|
+
export const isElementVisible = (el: any, holder?: any) => {
|
|
498
498
|
holder = holder || document.body;
|
|
499
499
|
const { top, bottom } = el.getBoundingClientRect();
|
|
500
500
|
const holderRect = holder.getBoundingClientRect();
|
|
@@ -575,7 +575,7 @@ export const timeSince = (
|
|
|
575
575
|
}
|
|
576
576
|
};
|
|
577
577
|
|
|
578
|
-
export const isDate = (value:
|
|
578
|
+
export const isDate = (value: any): boolean => {
|
|
579
579
|
if (toString.call(value) === '[object Date]') {
|
|
580
580
|
return true;
|
|
581
581
|
}
|
|
@@ -635,6 +635,10 @@ export const truncate = (input: string, max: number): string => {
|
|
|
635
635
|
};
|
|
636
636
|
|
|
637
637
|
export const oxford = (items: any[], joiner = 'and'): any => {
|
|
638
|
+
if (items.length === 0) {
|
|
639
|
+
return '';
|
|
640
|
+
}
|
|
641
|
+
|
|
638
642
|
if (items.length === 1) {
|
|
639
643
|
return items[0];
|
|
640
644
|
}
|
|
@@ -657,7 +661,9 @@ export const oxford = (items: any[], joiner = 'and'): any => {
|
|
|
657
661
|
});
|
|
658
662
|
}
|
|
659
663
|
|
|
660
|
-
|
|
664
|
+
const allButLast = items.slice(0, -1);
|
|
665
|
+
const last = items[items.length - 1];
|
|
666
|
+
return allButLast.join(', ') + ', ' + joiner + ' ' + last;
|
|
661
667
|
};
|
|
662
668
|
|
|
663
669
|
export const oxfordFn = (
|
|
@@ -713,13 +719,19 @@ export enum COOKIE_KEYS {
|
|
|
713
719
|
TICKET_SHOW_DETAILS = 'tickets.show-details'
|
|
714
720
|
}
|
|
715
721
|
|
|
716
|
-
export const capitalize = (
|
|
722
|
+
export const capitalize = (
|
|
723
|
+
[first, ...rest]: string[] | string,
|
|
724
|
+
locale = navigator.language
|
|
725
|
+
) =>
|
|
717
726
|
first === undefined ? '' : first.toLocaleUpperCase(locale) + rest.join('');
|
|
718
727
|
|
|
719
728
|
export const formatFileType = (type: string): string => {
|
|
720
729
|
return type.split('/')[1];
|
|
721
730
|
};
|
|
722
|
-
export const formatFileSize = (
|
|
731
|
+
export const formatFileSize = (
|
|
732
|
+
bytes: number,
|
|
733
|
+
decimalPoint?: number
|
|
734
|
+
): string => {
|
|
723
735
|
if (bytes == 0) return '0 KB';
|
|
724
736
|
const k = 1024,
|
|
725
737
|
dm = decimalPoint || 2,
|
|
@@ -127,9 +127,10 @@ export class VectorIcon extends LitElement {
|
|
|
127
127
|
|
|
128
128
|
.spin-forever {
|
|
129
129
|
animation-name: spin;
|
|
130
|
-
animation-duration: 2000ms;
|
|
130
|
+
animation-duration: var(--test-animation-duration, 2000ms);
|
|
131
131
|
animation-iteration-count: infinite;
|
|
132
132
|
animation-timing-function: linear;
|
|
133
|
+
animation-play-state: var(--test-animation-play-state, running);
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
@keyframes spin {
|
package/temba-modules.ts
CHANGED
|
@@ -58,7 +58,6 @@ import { MediaPicker } from './src/mediapicker/MediaPicker';
|
|
|
58
58
|
import { Editor } from './src/flow/Editor';
|
|
59
59
|
import { EditorNode } from './src/flow/EditorNode';
|
|
60
60
|
import { ContactNotepad } from './src/contacts/ContactNotepad';
|
|
61
|
-
import { OutboxMonitor } from './src/outboxmonitor/OutboxMonitor';
|
|
62
61
|
import { ProgressBar } from './src/progress/ProgressBar';
|
|
63
62
|
import { StartProgress } from './src/progress/StartProgress';
|
|
64
63
|
import { ShortcutList } from './src/list/ShortcutList';
|
|
@@ -134,7 +133,6 @@ addCustomElement('temba-media-picker', MediaPicker);
|
|
|
134
133
|
addCustomElement('temba-flow-editor', Editor);
|
|
135
134
|
addCustomElement('temba-flow-node', EditorNode);
|
|
136
135
|
addCustomElement('temba-contact-notepad', ContactNotepad);
|
|
137
|
-
addCustomElement('temba-outbox-monitor', OutboxMonitor);
|
|
138
136
|
addCustomElement('temba-progress', ProgressBar);
|
|
139
137
|
addCustomElement('temba-start-progress', StartProgress);
|
|
140
138
|
addCustomElement('temba-shortcuts', ShortcutList);
|