@nyaruka/temba-components 0.42.1 → 0.43.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 +8 -0
- package/dist/{dae3a0ce.js → 96498fd6.js} +226 -202
- package/dist/index.js +226 -202
- package/dist/sw.js +1 -1
- package/dist/sw.js.map +1 -1
- package/dist/templates/components-body.html +1 -1
- package/dist/templates/components-head.html +1 -1
- package/out-tsc/src/contacts/ContactChat.js +1 -2
- package/out-tsc/src/contacts/ContactChat.js.map +1 -1
- package/out-tsc/src/contacts/ContactHistory.js +17 -1
- package/out-tsc/src/contacts/ContactHistory.js.map +1 -1
- package/out-tsc/src/contacts/events.js +64 -58
- package/out-tsc/src/contacts/events.js.map +1 -1
- package/out-tsc/src/lightbox/Lightbox.js +146 -0
- package/out-tsc/src/lightbox/Lightbox.js.map +1 -0
- package/out-tsc/src/list/TembaMenu.js +2 -42
- package/out-tsc/src/list/TembaMenu.js.map +1 -1
- package/out-tsc/src/list/TicketList.js +2 -2
- package/out-tsc/src/list/TicketList.js.map +1 -1
- package/out-tsc/src/utils/index.js +54 -1
- package/out-tsc/src/utils/index.js.map +1 -1
- package/out-tsc/src/vectoricon/index.js +1 -1
- package/out-tsc/src/vectoricon/index.js.map +1 -1
- package/out-tsc/temba-modules.js +2 -0
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/temba-contact-chat.test.js +16 -2
- package/out-tsc/test/temba-contact-chat.test.js.map +1 -1
- package/out-tsc/test/temba-contact-history.test.js +1 -1
- package/out-tsc/test/temba-contact-history.test.js.map +1 -1
- package/out-tsc/test/temba-lightbox.test.js +28 -0
- package/out-tsc/test/temba-lightbox.test.js.map +1 -0
- package/out-tsc/test/utils.test.js +1 -2
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/contacts/compose-attachments-no-text-failure.png +0 -0
- package/screenshots/truth/contacts/compose-attachments-no-text-success.png +0 -0
- package/screenshots/truth/contacts/compose-text-and-attachments-failure-attachments.png +0 -0
- package/screenshots/truth/contacts/compose-text-and-attachments-failure-generic.png +0 -0
- package/screenshots/truth/contacts/compose-text-and-attachments-failure-text.png +0 -0
- package/screenshots/truth/contacts/compose-text-and-attachments-success.png +0 -0
- package/screenshots/truth/contacts/compose-text-no-attachments-failure.png +0 -0
- package/screenshots/truth/contacts/compose-text-no-attachments-success.png +0 -0
- package/screenshots/truth/contacts/contact-active-default.png +0 -0
- package/screenshots/truth/contacts/contact-active-show-chatbox.png +0 -0
- package/screenshots/truth/contacts/contact-active-ticket-closed-show-reopen-button.png +0 -0
- package/screenshots/truth/contacts/contact-active-ticket-open-show-chatbox.png +0 -0
- package/screenshots/truth/contacts/contact-archived-hide-chatbox.png +0 -0
- package/screenshots/truth/contacts/contact-archived-ticket-closed-hide-chatbox.png +0 -0
- package/screenshots/truth/contacts/contact-blocked-hide-chatbox.png +0 -0
- package/screenshots/truth/contacts/contact-stopped-hide-chatbox.png +0 -0
- package/screenshots/truth/contacts/history.png +0 -0
- package/screenshots/truth/lightbox/img-zoomed.png +0 -0
- package/screenshots/truth/lightbox/img.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/src/contacts/ContactChat.ts +1 -2
- package/src/contacts/ContactHistory.ts +19 -1
- package/src/contacts/events.ts +79 -64
- package/src/lightbox/Lightbox.ts +161 -0
- package/src/list/TembaMenu.ts +2 -45
- package/src/list/TicketList.ts +2 -2
- package/src/untyped.d.ts +1 -0
- package/src/utils/index.ts +68 -2
- package/src/vectoricon/index.ts +1 -1
- package/static/css/temba-components.css +4 -1
- package/temba-modules.ts +2 -0
- package/test/temba-contact-chat.test.ts +16 -3
- package/test/temba-contact-history.test.ts +1 -1
- package/test/temba-lightbox.test.ts +35 -0
- package/test/utils.test.ts +1 -2
- package/test-assets/img/meow.jpg +0 -0
- package/test-assets/style.css +1 -0
- package/web-test-runner.config.mjs +4 -0
- package/screenshots/truth/menu/menu-focus.png +0 -0
package/src/contacts/events.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { css, html, TemplateResult } from 'lit';
|
|
2
2
|
import { Msg, ObjectReference, User } from '../interfaces';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
getClasses,
|
|
5
|
+
oxford,
|
|
6
|
+
oxfordFn,
|
|
7
|
+
oxfordNamed,
|
|
8
|
+
renderAvatar,
|
|
9
|
+
timeSince,
|
|
10
|
+
} from '../utils';
|
|
4
11
|
import { Icon } from '../vectoricon';
|
|
5
12
|
import { getDisplayName } from './helpers';
|
|
6
13
|
|
|
@@ -165,11 +172,20 @@ export const getEventStyles = () => {
|
|
|
165
172
|
}
|
|
166
173
|
|
|
167
174
|
.msg {
|
|
168
|
-
|
|
169
|
-
border-radius: 8px;
|
|
175
|
+
border-radius: calc(var(--curvature) * 2.5);
|
|
170
176
|
border: 2px solid rgba(100, 100, 100, 0.1);
|
|
171
177
|
max-width: 300px;
|
|
172
178
|
word-break: break-word;
|
|
179
|
+
overflow: hidden;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.msg.attachments-1.no-message {
|
|
183
|
+
border: 2px solid transparent;
|
|
184
|
+
background-color: transparent !important;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.msg .text {
|
|
188
|
+
padding: var(--event-padding);
|
|
173
189
|
}
|
|
174
190
|
|
|
175
191
|
.event.msg_received .msg {
|
|
@@ -186,7 +202,13 @@ export const getEventStyles = () => {
|
|
|
186
202
|
.event.msg_created .msg,
|
|
187
203
|
.event.broadcast_created .msg,
|
|
188
204
|
.event.ivr_created .msg {
|
|
189
|
-
background:
|
|
205
|
+
background: var(--color-primary-dark);
|
|
206
|
+
color: white;
|
|
207
|
+
font-weight: 400;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.msg.automated {
|
|
211
|
+
background: var(--color-automated) !important;
|
|
190
212
|
}
|
|
191
213
|
|
|
192
214
|
.webhook_called {
|
|
@@ -439,7 +461,17 @@ export const getEventStyles = () => {
|
|
|
439
461
|
}
|
|
440
462
|
|
|
441
463
|
.attachments {
|
|
442
|
-
|
|
464
|
+
display: flex;
|
|
465
|
+
flex-wrap: wrap;
|
|
466
|
+
margin: -0.2em;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.attachment {
|
|
470
|
+
flex: 1 0 45%;
|
|
471
|
+
border-top: 0.05em solid transparent;
|
|
472
|
+
border-left: 0.05em solid transparent;
|
|
473
|
+
margin-top: 0.05em;
|
|
474
|
+
margin-left: 0.05em;
|
|
443
475
|
}
|
|
444
476
|
`;
|
|
445
477
|
};
|
|
@@ -636,36 +668,10 @@ export const getEventGroupType = (event: ContactEvent, ticket: string) => {
|
|
|
636
668
|
return 'verbose';
|
|
637
669
|
};
|
|
638
670
|
|
|
639
|
-
export const
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
><div class="avatar flow" style="margin-top:0.5em">
|
|
644
|
-
<temba-icon size="1" name="${Icon.flow_user}" /></div
|
|
645
|
-
></temba-tip>`;
|
|
646
|
-
} else {
|
|
647
|
-
return html`<temba-tip
|
|
648
|
-
text="${user.first_name + ' ' + user.last_name}"
|
|
649
|
-
position="top"
|
|
650
|
-
>
|
|
651
|
-
<div
|
|
652
|
-
class="avatar"
|
|
653
|
-
style="
|
|
654
|
-
border-radius: 9999px;
|
|
655
|
-
display:flex;
|
|
656
|
-
align-items:center;
|
|
657
|
-
border: 2px solid rgba(0,0,0,.05);
|
|
658
|
-
background: ${current ? 'var(--color-primary-dark)' : '#eee'};
|
|
659
|
-
color: ${current ? '#fff' : '#999'} ;
|
|
660
|
-
font-weight: 400;
|
|
661
|
-
padding: 0.5em;
|
|
662
|
-
line-height:1.2em;
|
|
663
|
-
"
|
|
664
|
-
>
|
|
665
|
-
${user.first_name[0] + user.last_name[0]}
|
|
666
|
-
</div>
|
|
667
|
-
</temba-tip>`;
|
|
668
|
-
}
|
|
671
|
+
export const renderUserAvatar = (user: User) => {
|
|
672
|
+
return html`<div style="width:3.5em;font-size:0.8em">
|
|
673
|
+
${renderAvatar({ user, position: 'left' })}
|
|
674
|
+
</div>`;
|
|
669
675
|
};
|
|
670
676
|
|
|
671
677
|
export const renderAttachment = (attachment: string): TemplateResult => {
|
|
@@ -676,10 +682,12 @@ export const renderAttachment = (attachment: string): TemplateResult => {
|
|
|
676
682
|
|
|
677
683
|
let inner = null;
|
|
678
684
|
if (mediaType === 'image') {
|
|
679
|
-
inner = html
|
|
685
|
+
inner = html`
|
|
686
|
+
<img src="${url}" style="height:auto;width:100%;display:block;" />
|
|
687
|
+
`;
|
|
680
688
|
} else if (ext === 'pdf') {
|
|
681
689
|
return html`<div
|
|
682
|
-
style="width:100%;height:300px;border-radius:var(--curvature);box-shadow:0px 0px 12px 0px rgba(0,0,0,.1), 0px 0px 2px 0px rgba(0,0,0,.15);overflow:hidden"
|
|
690
|
+
style="width:100%;height:300px;border-radius:calc(var(--curvature) * 2.5);box-shadow:0px 0px 12px 0px rgba(0,0,0,.1), 0px 0px 2px 0px rgba(0,0,0,.15);overflow:hidden"
|
|
683
691
|
><embed src="${url}#view=Fit" type="application/pdf" frameBorder="0" scrolling="auto" height="100%" width="100%"></embed></div>`;
|
|
684
692
|
} else if (mediaType === 'video') {
|
|
685
693
|
return html`<video
|
|
@@ -724,11 +732,7 @@ export const renderAttachment = (attachment: string): TemplateResult => {
|
|
|
724
732
|
</div>`;
|
|
725
733
|
}
|
|
726
734
|
|
|
727
|
-
return html`<div
|
|
728
|
-
style="width:100%;max-width:300px;border-radius:var(--curvature); box-shadow:0px 0px 6px 0px rgba(0,0,0,.15);overflow:hidden"
|
|
729
|
-
>
|
|
730
|
-
${inner}
|
|
731
|
-
</div>`;
|
|
735
|
+
return html`<div style="">${inner}</div>`;
|
|
732
736
|
};
|
|
733
737
|
|
|
734
738
|
export const renderMsgEvent = (
|
|
@@ -741,6 +745,7 @@ export const renderMsgEvent = (
|
|
|
741
745
|
|
|
742
746
|
// summary items which appear under the message bubble
|
|
743
747
|
const summary: TemplateResult[] = [];
|
|
748
|
+
|
|
744
749
|
if (event.logs_url) {
|
|
745
750
|
summary.push(html` <div class="icon-link">
|
|
746
751
|
<temba-icon
|
|
@@ -786,17 +791,32 @@ export const renderMsgEvent = (
|
|
|
786
791
|
|
|
787
792
|
return html`<div style="display:flex;align-items:flex-start">
|
|
788
793
|
<div style="display:flex;flex-direction:column">
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
794
|
+
<div
|
|
795
|
+
class="${event.msg.text ? '' : 'no-message'} attachments-${(
|
|
796
|
+
event.msg.attachments || []
|
|
797
|
+
).length} ${getClasses({
|
|
798
|
+
msg: true,
|
|
799
|
+
automated: !isInbound && !event.msg.created_by,
|
|
800
|
+
})}"
|
|
801
|
+
>
|
|
802
|
+
${event.msg.text
|
|
803
|
+
? html` <div class="text">${event.msg.text}</div> `
|
|
804
|
+
: null}
|
|
805
|
+
${event.msg.attachments
|
|
806
|
+
? html`<div class="attachments">
|
|
807
|
+
${event.msg.attachments.map(
|
|
808
|
+
attachment =>
|
|
809
|
+
html` <div class="attachment">
|
|
810
|
+
${renderAttachment(attachment)}
|
|
811
|
+
</div>`
|
|
812
|
+
)}
|
|
813
|
+
</div> `
|
|
814
|
+
: null}
|
|
815
|
+
</div>
|
|
797
816
|
${!event.msg.text && !event.msg.attachments
|
|
798
817
|
? html`<div class="unsupported">Unsupported Message</div>`
|
|
799
818
|
: null}
|
|
819
|
+
|
|
800
820
|
<div
|
|
801
821
|
class="msg-summary"
|
|
802
822
|
style="flex-direction:row${isInbound ? '-reverse' : ''}"
|
|
@@ -807,12 +827,10 @@ export const renderMsgEvent = (
|
|
|
807
827
|
</div>
|
|
808
828
|
|
|
809
829
|
${!isInbound
|
|
810
|
-
? html`<div style="margin-left:0.8em;margin-top:0.3em">
|
|
830
|
+
? html`<div style="margin-left:0.8em;margin-top:0.3em;font-size:0.9em">
|
|
811
831
|
${event.msg.created_by
|
|
812
|
-
?
|
|
813
|
-
|
|
814
|
-
</div>`
|
|
815
|
-
: renderAvatar({ email: FLOW_USER_ID })}
|
|
832
|
+
? renderUserAvatar(event.msg.created_by)
|
|
833
|
+
: renderUserAvatar(null)}
|
|
816
834
|
</div>`
|
|
817
835
|
: null}
|
|
818
836
|
</div>`;
|
|
@@ -934,10 +952,7 @@ export const renderLabelsAdded = (event: LabelsAddedEvent): TemplateResult => {
|
|
|
934
952
|
`;
|
|
935
953
|
};
|
|
936
954
|
|
|
937
|
-
export const renderNoteCreated = (
|
|
938
|
-
event: TicketEvent,
|
|
939
|
-
agent: string
|
|
940
|
-
): TemplateResult => {
|
|
955
|
+
export const renderNoteCreated = (event: TicketEvent): TemplateResult => {
|
|
941
956
|
return html`<div style="display:flex;align-items:flex-start">
|
|
942
957
|
<div style="display:flex;flex-direction:column">
|
|
943
958
|
<div class="description">${event.note}</div>
|
|
@@ -951,7 +966,7 @@ export const renderNoteCreated = (
|
|
|
951
966
|
</div>
|
|
952
967
|
</div>
|
|
953
968
|
<div style="margin-left:0.8em;margin-top:0.3em;font-size:0.8em">
|
|
954
|
-
${
|
|
969
|
+
${renderUserAvatar(event.created_by)}
|
|
955
970
|
</div>
|
|
956
971
|
</div>`;
|
|
957
972
|
};
|
|
@@ -1187,16 +1202,16 @@ export const renderCampaignFiredEvent = (
|
|
|
1187
1202
|
Campaign
|
|
1188
1203
|
<span
|
|
1189
1204
|
class="linked"
|
|
1190
|
-
onclick="goto(event)"
|
|
1191
|
-
href="/campaign/read/${event.campaign.uuid}"
|
|
1205
|
+
onclick="goto(event, this)"
|
|
1206
|
+
href="/campaign/read/${event.campaign.uuid}/"
|
|
1192
1207
|
>${event.campaign.name}</span
|
|
1193
1208
|
>
|
|
1194
1209
|
${event.fired_result === 'S' ? 'skipped' : 'triggered'}
|
|
1195
1210
|
<span
|
|
1196
1211
|
class="linked"
|
|
1197
|
-
onclick="goto(event)"
|
|
1212
|
+
onclick="goto(event, this)"
|
|
1198
1213
|
href="/campaignevent/read/${event.campaign.uuid}/${event.campaign_event
|
|
1199
|
-
.id}"
|
|
1214
|
+
.id}/"
|
|
1200
1215
|
>
|
|
1201
1216
|
${event.campaign_event.offset_display}
|
|
1202
1217
|
${event.campaign_event.relative_to.name}</span
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { css, html, PropertyValueMap } from 'lit';
|
|
2
|
+
import { property } from 'lit/decorators.js';
|
|
3
|
+
import { RapidElement } from '../RapidElement';
|
|
4
|
+
import { getClasses } from '../utils';
|
|
5
|
+
import { styleMap } from 'lit-html/directives/style-map.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* This component relies on a bit of sleight of hand magic
|
|
9
|
+
* to achieve it's effect. As such, it requires the use of
|
|
10
|
+
* computed animation times and window.setTimeout().
|
|
11
|
+
*/
|
|
12
|
+
export class Lightbox extends RapidElement {
|
|
13
|
+
static get styles() {
|
|
14
|
+
return css`
|
|
15
|
+
:host {
|
|
16
|
+
position: absolute;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.mask {
|
|
20
|
+
display: flex;
|
|
21
|
+
opacity: 0;
|
|
22
|
+
background: rgba(0, 0, 0, 0.5);
|
|
23
|
+
position: absolute;
|
|
24
|
+
height: 100vh;
|
|
25
|
+
width: 100vw;
|
|
26
|
+
pointer-events: none;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.zoom .mask {
|
|
30
|
+
opacity: 1;
|
|
31
|
+
pointer-events: auto;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.matte {
|
|
35
|
+
position: absolute;
|
|
36
|
+
transform: translate(400, 400) scale(3, 3);
|
|
37
|
+
border-radius: 2%;
|
|
38
|
+
overflow: hidden;
|
|
39
|
+
box-shadow: 0 0 12px 3px rgba(0, 0, 0, 0.15);
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@property({ type: Number })
|
|
45
|
+
animationTime = 300;
|
|
46
|
+
|
|
47
|
+
@property({ type: Boolean })
|
|
48
|
+
show = false;
|
|
49
|
+
|
|
50
|
+
@property({ type: Boolean })
|
|
51
|
+
zoom = false;
|
|
52
|
+
|
|
53
|
+
@property({ type: Number })
|
|
54
|
+
zoomPct = 0.9;
|
|
55
|
+
|
|
56
|
+
private ele: HTMLElement;
|
|
57
|
+
private left: number;
|
|
58
|
+
private top: number;
|
|
59
|
+
private height: number;
|
|
60
|
+
private width: number;
|
|
61
|
+
private scale = 1;
|
|
62
|
+
private xTrans = '0px';
|
|
63
|
+
private yTrans = '0px';
|
|
64
|
+
|
|
65
|
+
protected updated(
|
|
66
|
+
changed: PropertyValueMap<any> | Map<PropertyKey, unknown>
|
|
67
|
+
): void {
|
|
68
|
+
if (changed.has('show') && this.show) {
|
|
69
|
+
window.setTimeout(() => {
|
|
70
|
+
this.zoom = true;
|
|
71
|
+
}, 0);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (changed.has('zoom') && !this.zoom && this.show) {
|
|
75
|
+
window.setTimeout(() => {
|
|
76
|
+
this.show = false;
|
|
77
|
+
}, this.animationTime);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public showElement(ele: HTMLElement) {
|
|
82
|
+
// size our matte according to the ele's boundaries
|
|
83
|
+
const bounds = ele.getBoundingClientRect();
|
|
84
|
+
this.ele = ele.cloneNode() as HTMLElement;
|
|
85
|
+
this.left = bounds.left;
|
|
86
|
+
this.top = bounds.top;
|
|
87
|
+
this.width = bounds.width;
|
|
88
|
+
this.height = bounds.height;
|
|
89
|
+
|
|
90
|
+
this.xTrans = '0px';
|
|
91
|
+
this.yTrans = '0px';
|
|
92
|
+
this.scale = 1;
|
|
93
|
+
|
|
94
|
+
let desiredWidth = this.width;
|
|
95
|
+
let desiredHeight = this.height;
|
|
96
|
+
|
|
97
|
+
// set our destination and size
|
|
98
|
+
if (this.height > this.width) {
|
|
99
|
+
desiredHeight = window.innerHeight * this.zoomPct;
|
|
100
|
+
this.scale = desiredHeight / this.height;
|
|
101
|
+
desiredWidth = this.width * this.scale;
|
|
102
|
+
}
|
|
103
|
+
// landscape
|
|
104
|
+
else {
|
|
105
|
+
desiredWidth = window.innerWidth * this.zoomPct;
|
|
106
|
+
this.scale = desiredWidth / this.width;
|
|
107
|
+
desiredHeight = this.height * this.scale;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const xGrowth = (desiredWidth - this.width) / 2;
|
|
111
|
+
const xDest = (window.innerWidth - desiredWidth) / 2;
|
|
112
|
+
this.xTrans = xDest - this.left + xGrowth + 'px';
|
|
113
|
+
|
|
114
|
+
const yGrowth = (desiredHeight - this.height) / 2;
|
|
115
|
+
const yDest = (window.innerHeight - desiredHeight) / 2;
|
|
116
|
+
this.yTrans = yDest - this.top + yGrowth + 'px';
|
|
117
|
+
this.show = true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public handleClick() {
|
|
121
|
+
this.zoom = false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
public render() {
|
|
125
|
+
const styles = {
|
|
126
|
+
transition: `transform ${this.animationTime}ms ease, box-shadow ${this.animationTime}ms ease`,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
if (this.show) {
|
|
130
|
+
styles['left'] = this.left + 'px';
|
|
131
|
+
styles['top'] = this.top + 'px';
|
|
132
|
+
styles['width'] = this.width + 'px';
|
|
133
|
+
styles['height'] = this.height + 'px';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (this.zoom) {
|
|
137
|
+
styles[
|
|
138
|
+
'transform'
|
|
139
|
+
] = `translate(${this.xTrans}, ${this.yTrans}) scale(${this.scale}, ${this.scale})`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return html`
|
|
143
|
+
<div
|
|
144
|
+
class=${getClasses({
|
|
145
|
+
container: true,
|
|
146
|
+
show: this.show,
|
|
147
|
+
zoom: this.zoom,
|
|
148
|
+
})}
|
|
149
|
+
@click=${this.handleClick}
|
|
150
|
+
>
|
|
151
|
+
<div
|
|
152
|
+
class=${getClasses({ mask: true })}
|
|
153
|
+
style="transition: all ${this.animationTime}ms; ease"
|
|
154
|
+
></div>
|
|
155
|
+
<div class=${getClasses({ matte: true })} style=${styleMap(styles)}>
|
|
156
|
+
${this.show ? this.ele : null}
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
`;
|
|
160
|
+
}
|
|
161
|
+
}
|
package/src/list/TembaMenu.ts
CHANGED
|
@@ -2,9 +2,8 @@ import { css, html, TemplateResult } from 'lit';
|
|
|
2
2
|
import { property } from 'lit/decorators.js';
|
|
3
3
|
import { CustomEventType } from '../interfaces';
|
|
4
4
|
import { RapidElement } from '../RapidElement';
|
|
5
|
-
import { debounce, fetchResults, getClasses } from '../utils';
|
|
5
|
+
import { debounce, fetchResults, getClasses, renderAvatar } from '../utils';
|
|
6
6
|
import { Icon } from '../vectoricon';
|
|
7
|
-
import ColorHash from 'color-hash';
|
|
8
7
|
|
|
9
8
|
export interface MenuItem {
|
|
10
9
|
id?: string;
|
|
@@ -166,19 +165,6 @@ export class TembaMenu extends RapidElement {
|
|
|
166
165
|
align-items: center;
|
|
167
166
|
}
|
|
168
167
|
|
|
169
|
-
.avatar-circle {
|
|
170
|
-
box-shadow: 0 0 0px 3px rgba(0, 0, 0, 0.075);
|
|
171
|
-
display: flex;
|
|
172
|
-
margin: 0.4em 0em;
|
|
173
|
-
height: 2em;
|
|
174
|
-
width: 2em;
|
|
175
|
-
flex-direction: row;
|
|
176
|
-
align-items: center;
|
|
177
|
-
color: #fff;
|
|
178
|
-
border-radius: 100%;
|
|
179
|
-
font-weight: 400;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
168
|
temba-dropdown > div[slot='dropdown'] .avatar .avatar-circle {
|
|
183
169
|
font-size: 0.7em;
|
|
184
170
|
}
|
|
@@ -836,35 +822,6 @@ export class TembaMenu extends RapidElement {
|
|
|
836
822
|
return expanded;
|
|
837
823
|
}
|
|
838
824
|
|
|
839
|
-
private renderAvatar(avatar: string) {
|
|
840
|
-
const hash = new ColorHash();
|
|
841
|
-
const color = hash.hex(avatar);
|
|
842
|
-
|
|
843
|
-
let second = avatar.indexOf(' ') + 1;
|
|
844
|
-
if (second < 1) {
|
|
845
|
-
second = avatar.length > 1 ? 1 : 0;
|
|
846
|
-
}
|
|
847
|
-
let name = avatar.substring(0, 1) + avatar.substring(second, second + 1);
|
|
848
|
-
name = name.toUpperCase();
|
|
849
|
-
|
|
850
|
-
return html`
|
|
851
|
-
<div
|
|
852
|
-
style="border: 0px solid red; display:flex; flex-direction: column; align-items:center;"
|
|
853
|
-
>
|
|
854
|
-
<div
|
|
855
|
-
class="avatar-circle"
|
|
856
|
-
style="border: 0px solid rgba(0,0,0,.1);background:${color}"
|
|
857
|
-
>
|
|
858
|
-
<div
|
|
859
|
-
style="border: 0px solid red; display:flex; flex-direction: column; align-items:center;flex-grow:1"
|
|
860
|
-
>
|
|
861
|
-
<div style="border:0px solid blue;">${name}</div>
|
|
862
|
-
</div>
|
|
863
|
-
</div>
|
|
864
|
-
</div>
|
|
865
|
-
`;
|
|
866
|
-
}
|
|
867
|
-
|
|
868
825
|
private renderMenuItem = (
|
|
869
826
|
menuItem: MenuItem,
|
|
870
827
|
parent: MenuItem = null
|
|
@@ -929,7 +886,7 @@ export class TembaMenu extends RapidElement {
|
|
|
929
886
|
});
|
|
930
887
|
|
|
931
888
|
if (menuItem.avatar) {
|
|
932
|
-
icon =
|
|
889
|
+
icon = renderAvatar({ name: menuItem.avatar });
|
|
933
890
|
}
|
|
934
891
|
|
|
935
892
|
const item = html` <div
|
package/src/list/TicketList.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { property } from 'lit/decorators.js';
|
|
|
3
3
|
import { TembaList } from './TembaList';
|
|
4
4
|
import { timeSince } from '../utils';
|
|
5
5
|
import { Contact } from '../interfaces';
|
|
6
|
-
import {
|
|
6
|
+
import { renderUserAvatar } from '../contacts/events';
|
|
7
7
|
import { Icon } from '../vectoricon';
|
|
8
8
|
|
|
9
9
|
export class TicketList extends TembaList {
|
|
@@ -84,7 +84,7 @@ export class TicketList extends TembaList {
|
|
|
84
84
|
</div>
|
|
85
85
|
<div style="font-size:0.7em;">
|
|
86
86
|
${!contact.ticket.closed_on && contact.ticket.assignee
|
|
87
|
-
? html`${
|
|
87
|
+
? html`${renderUserAvatar(contact.ticket.assignee)}`
|
|
88
88
|
: null}
|
|
89
89
|
</div>
|
|
90
90
|
</div>
|
package/src/untyped.d.ts
CHANGED
package/src/utils/index.ts
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
import { html, TemplateResult } from 'lit-html';
|
|
3
3
|
import { Button } from '../button/Button';
|
|
4
4
|
import { Dialog } from '../dialog/Dialog';
|
|
5
|
-
import { ContactField, Ticket } from '../interfaces';
|
|
5
|
+
import { ContactField, Ticket, User } from '../interfaces';
|
|
6
|
+
import ColorHash from 'color-hash';
|
|
7
|
+
|
|
8
|
+
const colorHash = new ColorHash();
|
|
6
9
|
|
|
7
10
|
export type Asset = KeyedAsset & Ticket & ContactField;
|
|
8
11
|
|
|
@@ -108,7 +111,7 @@ export const getClasses = (map: ClassMap): string => {
|
|
|
108
111
|
if (result.trim().length > 0) {
|
|
109
112
|
result = ' ' + result;
|
|
110
113
|
}
|
|
111
|
-
return result;
|
|
114
|
+
return result.trim();
|
|
112
115
|
};
|
|
113
116
|
|
|
114
117
|
export const fetchResultsPage = (
|
|
@@ -613,3 +616,66 @@ export const formatFileSize = (bytes: number, decimalPoint: number): string => {
|
|
|
613
616
|
i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
614
617
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
|
615
618
|
};
|
|
619
|
+
|
|
620
|
+
export const renderAvatar = (input: {
|
|
621
|
+
name?: string;
|
|
622
|
+
user?: User;
|
|
623
|
+
icon?: string;
|
|
624
|
+
image?: string;
|
|
625
|
+
position?: string;
|
|
626
|
+
}) => {
|
|
627
|
+
if (!input.position) {
|
|
628
|
+
input.position = 'right';
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// just a url
|
|
632
|
+
if (input.image) {
|
|
633
|
+
return html`<img src="${input.image}" />`;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
let text = input.name;
|
|
637
|
+
if (input.user) {
|
|
638
|
+
text = `${input.user.first_name} ${input.user.last_name}`;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
if (!text) {
|
|
642
|
+
return null;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const color = colorHash.hex(text);
|
|
646
|
+
let second = text.indexOf(' ') + 1;
|
|
647
|
+
if (second < 1) {
|
|
648
|
+
second = text.length > 1 ? 1 : 0;
|
|
649
|
+
}
|
|
650
|
+
let initials = text.substring(0, 1) + text.substring(second, second + 1);
|
|
651
|
+
initials = initials.toUpperCase();
|
|
652
|
+
|
|
653
|
+
return html`
|
|
654
|
+
<temba-tip text=${text} position=${input.position}>
|
|
655
|
+
<div
|
|
656
|
+
style="border: 0px solid red; display:flex; flex-direction: column; align-items:center;"
|
|
657
|
+
>
|
|
658
|
+
<div
|
|
659
|
+
class="avatar-circle"
|
|
660
|
+
style="
|
|
661
|
+
display: flex;
|
|
662
|
+
height: 2em;
|
|
663
|
+
width: 2em;
|
|
664
|
+
flex-direction: row;
|
|
665
|
+
align-items: center;
|
|
666
|
+
color: #fff;
|
|
667
|
+
border-radius: 100%;
|
|
668
|
+
font-weight: 400;
|
|
669
|
+
border: 0.3em solid rgba(0,0,0,.05);
|
|
670
|
+
background:${color}"
|
|
671
|
+
>
|
|
672
|
+
<div
|
|
673
|
+
style="border: 0px solid red; display:flex; flex-direction: column; align-items:center;flex-grow:1"
|
|
674
|
+
>
|
|
675
|
+
<div style="border:0px solid blue;">${initials}</div>
|
|
676
|
+
</div>
|
|
677
|
+
</div>
|
|
678
|
+
</div>
|
|
679
|
+
</temba-tip>
|
|
680
|
+
`;
|
|
681
|
+
};
|
package/src/vectoricon/index.ts
CHANGED
|
@@ -105,6 +105,8 @@
|
|
|
105
105
|
--color-error: rgb(var(--error-rgb));
|
|
106
106
|
--color-alert: rgb(var(--error-rgb));
|
|
107
107
|
|
|
108
|
+
--color-automated: rgb(78,205,106);
|
|
109
|
+
|
|
108
110
|
--icon-color: var(--text-color);
|
|
109
111
|
--icon-color-hover: var(--icon-color);
|
|
110
112
|
--icon-color-circle-hover: rgba(245, 245, 245, .8);
|
|
@@ -139,7 +141,6 @@
|
|
|
139
141
|
--button-y: 6px;
|
|
140
142
|
--button-x: 14px;
|
|
141
143
|
|
|
142
|
-
--menu-padding: 1em;
|
|
143
144
|
--bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
|
144
145
|
|
|
145
146
|
--temba-charcount-counts-margin-top: 4px;
|
|
@@ -148,6 +149,8 @@
|
|
|
148
149
|
--temba-charcount-summary-right: 0px;
|
|
149
150
|
--temba-charcount-summary-bottom: 0px;
|
|
150
151
|
|
|
152
|
+
--color-automated: rgb(78,205,106);
|
|
153
|
+
|
|
151
154
|
}
|
|
152
155
|
|
|
153
156
|
temba-button {
|
package/temba-modules.ts
CHANGED
|
@@ -45,6 +45,7 @@ import { ContentMenu } from './src/list/ContentMenu';
|
|
|
45
45
|
import { TembaDate } from './src/date/TembaDate';
|
|
46
46
|
import Remote from './src/remote/Remote';
|
|
47
47
|
import { Compose } from './src/compose/Compose';
|
|
48
|
+
import { Lightbox } from './src/lightbox/Lightbox';
|
|
48
49
|
|
|
49
50
|
export function addCustomElement(name: string, comp: any) {
|
|
50
51
|
if (!window.customElements.get(name)) {
|
|
@@ -63,6 +64,7 @@ addCustomElement('temba-checkbox', Checkbox);
|
|
|
63
64
|
addCustomElement('temba-select', Select);
|
|
64
65
|
addCustomElement('temba-options', Options);
|
|
65
66
|
addCustomElement('temba-loading', Loading);
|
|
67
|
+
addCustomElement('temba-lightbox', Lightbox);
|
|
66
68
|
addCustomElement('temba-button', Button);
|
|
67
69
|
addCustomElement('temba-omnibox', Omnibox);
|
|
68
70
|
addCustomElement('temba-tip', Tip);
|
|
@@ -536,13 +536,20 @@ describe('temba-contact-chat - ticket tests', () => {
|
|
|
536
536
|
chat.refresh();
|
|
537
537
|
await chat.httpComplete;
|
|
538
538
|
|
|
539
|
+
let lastScroll = 0;
|
|
539
540
|
await assertScreenshot(
|
|
540
541
|
'contacts/contact-active-ticket-closed-show-reopen-button',
|
|
541
542
|
getClip(chat),
|
|
542
543
|
{
|
|
543
544
|
clock: clock,
|
|
544
545
|
predicate: () => {
|
|
545
|
-
|
|
546
|
+
const currentScroll = chat
|
|
547
|
+
.getContactHistory()
|
|
548
|
+
.getEventsPane().scrollTop;
|
|
549
|
+
if (currentScroll !== 0 && currentScroll === lastScroll) {
|
|
550
|
+
return true;
|
|
551
|
+
}
|
|
552
|
+
lastScroll = currentScroll;
|
|
546
553
|
},
|
|
547
554
|
}
|
|
548
555
|
);
|
|
@@ -562,14 +569,20 @@ describe('temba-contact-chat - ticket tests', () => {
|
|
|
562
569
|
chat.currentTicket = tickets.items[0];
|
|
563
570
|
chat.refresh();
|
|
564
571
|
await chat.httpComplete;
|
|
565
|
-
|
|
572
|
+
let lastScroll = 0;
|
|
566
573
|
await assertScreenshot(
|
|
567
574
|
'contacts/contact-archived-ticket-closed-hide-chatbox',
|
|
568
575
|
getClip(chat),
|
|
569
576
|
{
|
|
570
577
|
clock: clock,
|
|
571
578
|
predicate: () => {
|
|
572
|
-
|
|
579
|
+
const currentScroll = chat
|
|
580
|
+
.getContactHistory()
|
|
581
|
+
.getEventsPane().scrollTop;
|
|
582
|
+
if (currentScroll !== 0 && currentScroll === lastScroll) {
|
|
583
|
+
return true;
|
|
584
|
+
}
|
|
585
|
+
lastScroll = currentScroll;
|
|
573
586
|
},
|
|
574
587
|
}
|
|
575
588
|
);
|