@nyaruka/temba-components 0.108.7 → 0.109.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 +19 -2
- package/dist/static/svg/index.svg +1 -1
- package/dist/temba-components.js +602 -455
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/charcount/CharCount.js +4 -5
- package/out-tsc/src/charcount/CharCount.js.map +1 -1
- package/out-tsc/src/completion/Completion.js +27 -16
- package/out-tsc/src/completion/Completion.js.map +1 -1
- package/out-tsc/src/compose/Compose.js +259 -95
- package/out-tsc/src/compose/Compose.js.map +1 -1
- package/out-tsc/src/contacts/ContactChat.js +18 -16
- package/out-tsc/src/contacts/ContactChat.js.map +1 -1
- package/out-tsc/src/contacts/ContactTickets.js +1 -1
- package/out-tsc/src/contacts/ContactTickets.js.map +1 -1
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/list/ShortcutList.js +125 -0
- package/out-tsc/src/list/ShortcutList.js.map +1 -0
- package/out-tsc/src/list/TembaList.js +8 -5
- package/out-tsc/src/list/TembaList.js.map +1 -1
- package/out-tsc/src/options/Options.js +46 -35
- package/out-tsc/src/options/Options.js.map +1 -1
- package/out-tsc/src/select/Select.js +1 -1
- package/out-tsc/src/select/Select.js.map +1 -1
- package/out-tsc/src/store/Store.js +18 -3
- package/out-tsc/src/store/Store.js.map +1 -1
- package/out-tsc/src/tabpane/Tab.js +2 -0
- package/out-tsc/src/tabpane/Tab.js.map +1 -1
- package/out-tsc/src/tabpane/TabPane.js +27 -5
- package/out-tsc/src/tabpane/TabPane.js.map +1 -1
- package/out-tsc/src/textinput/TextInput.js +7 -2
- package/out-tsc/src/textinput/TextInput.js.map +1 -1
- package/out-tsc/src/utils/index.js.map +1 -1
- package/out-tsc/src/vectoricon/index.js +2 -2
- 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-compose.test.js +26 -18
- package/out-tsc/test/temba-compose.test.js.map +1 -1
- package/out-tsc/test/temba-contact-chat.test.js +27 -18
- package/out-tsc/test/temba-contact-chat.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/compose/attachments-and-send-button.png +0 -0
- package/screenshots/truth/compose/attachments-no-send-button.png +0 -0
- package/screenshots/truth/compose/attachments-with-all-files-and-click-send.png +0 -0
- package/screenshots/truth/compose/attachments-with-all-files.png +0 -0
- package/screenshots/truth/compose/attachments-with-failure-files.png +0 -0
- package/screenshots/truth/compose/attachments-with-success-files-and-click-send.png +0 -0
- package/screenshots/truth/compose/attachments-with-success-files.png +0 -0
- package/screenshots/truth/compose/chatbox-attachments-counter-and-send-button.png +0 -0
- package/screenshots/truth/compose/chatbox-attachments-counter-no-send-button.png +0 -0
- package/screenshots/truth/compose/chatbox-attachments-no-counter-and-send-button.png +0 -0
- package/screenshots/truth/compose/chatbox-attachments-no-counter-no-send-button.png +0 -0
- package/screenshots/truth/compose/chatbox-counter-and-send-button.png +0 -0
- package/screenshots/truth/compose/chatbox-counter-no-send-button.png +0 -0
- package/screenshots/truth/compose/chatbox-no-counter-and-send-button.png +0 -0
- package/screenshots/truth/compose/chatbox-no-counter-no-send-button.png +0 -0
- package/screenshots/truth/compose/chatbox-no-text-attachments-with-all-files-and-click-send.png +0 -0
- package/screenshots/truth/compose/chatbox-no-text-attachments-with-all-files.png +0 -0
- package/screenshots/truth/compose/chatbox-no-text-attachments-with-failure-files.png +0 -0
- package/screenshots/truth/compose/chatbox-no-text-attachments-with-success-files-and-click-send.png +0 -0
- package/screenshots/truth/compose/chatbox-no-text-attachments-with-success-files.png +0 -0
- package/screenshots/truth/compose/chatbox-with-text-and-click-send.png +0 -0
- package/screenshots/truth/compose/chatbox-with-text-and-hit-enter.png +0 -0
- package/screenshots/truth/compose/chatbox-with-text-and-spaces.png +0 -0
- package/screenshots/truth/compose/chatbox-with-text-and-url.png +0 -0
- package/screenshots/truth/compose/chatbox-with-text-attachments-no-files-and-click-send.png +0 -0
- package/screenshots/truth/compose/chatbox-with-text-attachments-no-files-and-hit-enter.png +0 -0
- package/screenshots/truth/compose/chatbox-with-text-attachments-no-files.png +0 -0
- package/screenshots/truth/compose/chatbox-with-text-attachments-with-all-files-and-click-send.png +0 -0
- package/screenshots/truth/compose/chatbox-with-text-attachments-with-all-files-and-hit-enter.png +0 -0
- package/screenshots/truth/compose/chatbox-with-text-attachments-with-all-files.png +0 -0
- package/screenshots/truth/compose/chatbox-with-text-attachments-with-failure-files.png +0 -0
- package/screenshots/truth/compose/chatbox-with-text-attachments-with-success-files-and-click-send.png +0 -0
- package/screenshots/truth/compose/chatbox-with-text-attachments-with-success-files-and-hit-enter.png +0 -0
- package/screenshots/truth/compose/chatbox-with-text-attachments-with-success-files.png +0 -0
- package/screenshots/truth/compose/chatbox-with-text-no-spaces.png +0 -0
- package/screenshots/truth/compose/chatbox-with-text.png +0 -0
- 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-and-attachments.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/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/src/charcount/CharCount.ts +4 -5
- package/src/completion/Completion.ts +33 -19
- package/src/compose/Compose.ts +289 -96
- package/src/contacts/ContactChat.ts +18 -16
- package/src/contacts/ContactTickets.ts +1 -1
- package/src/interfaces.ts +7 -0
- package/src/list/ShortcutList.ts +137 -0
- package/src/list/TembaList.ts +9 -6
- package/src/options/Options.ts +53 -44
- package/src/select/Select.ts +1 -1
- package/src/store/Store.ts +23 -4
- package/src/tabpane/Tab.ts +2 -0
- package/src/tabpane/TabPane.ts +28 -5
- package/src/textinput/TextInput.ts +9 -3
- package/src/utils/index.ts +8 -2
- package/src/vectoricon/index.ts +2 -2
- package/static/svg/index.svg +1 -1
- package/static/svg/work/traced/zap-fast.svg +1 -0
- package/static/svg/work/used/zap-fast.svg +3 -0
- package/temba-modules.ts +2 -0
- package/test/temba-compose.test.ts +28 -35
- package/test/temba-contact-chat.test.ts +28 -37
- package/test-assets/store/shortcuts.json +14 -0
- package/static/svg/work/traced/message-dots-circle.svg +0 -1
- package/static/svg/work/used/message-dots-circle.svg +0 -3
|
@@ -269,17 +269,16 @@ export class ContactChat extends ContactStoreElement {
|
|
|
269
269
|
min-height: 0;
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
-
.
|
|
272
|
+
.compose {
|
|
273
273
|
background: #fff;
|
|
274
274
|
display: flex;
|
|
275
275
|
flex-direction: column;
|
|
276
|
-
--textarea-min-height:
|
|
277
|
-
--textarea-height:
|
|
276
|
+
--textarea-min-height: 8em;
|
|
277
|
+
--textarea-height: 0.5em;
|
|
278
278
|
--widget-box-shadow-focused: none;
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
border-bottom-right-radius: 0 !important;
|
|
279
|
+
--compose-curvature: 0px;
|
|
280
|
+
border-top: 1px solid #e6e6e6;
|
|
281
|
+
overflow: hidden;
|
|
283
282
|
}
|
|
284
283
|
|
|
285
284
|
.closed-footer {
|
|
@@ -314,8 +313,11 @@ export class ContactChat extends ContactStoreElement {
|
|
|
314
313
|
}
|
|
315
314
|
|
|
316
315
|
.border {
|
|
317
|
-
|
|
318
|
-
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
temba-compose {
|
|
319
|
+
border-top-right-radius: 0;
|
|
320
|
+
border-top-left-radius: 0;
|
|
319
321
|
}
|
|
320
322
|
`;
|
|
321
323
|
}
|
|
@@ -487,10 +489,10 @@ export class ContactChat extends ContactStoreElement {
|
|
|
487
489
|
const contactHistory = this.currentContact
|
|
488
490
|
? this.getTembaContactHistory()
|
|
489
491
|
: null;
|
|
490
|
-
const
|
|
492
|
+
const tembaCompose = this.currentContact ? this.getTembaCompose() : null;
|
|
491
493
|
|
|
492
494
|
const contactHistoryAndChatbox = html`
|
|
493
|
-
<div class="chat-wrapper">${contactHistory} ${
|
|
495
|
+
<div class="chat-wrapper">${contactHistory} ${tembaCompose}</div>
|
|
494
496
|
`;
|
|
495
497
|
return html`${contactHistoryAndChatbox}`;
|
|
496
498
|
}
|
|
@@ -851,7 +853,7 @@ export class ContactChat extends ContactStoreElement {
|
|
|
851
853
|
></temba-chat>`;
|
|
852
854
|
}
|
|
853
855
|
|
|
854
|
-
private
|
|
856
|
+
private getTembaCompose(): TemplateResult {
|
|
855
857
|
if (this.currentTicket) {
|
|
856
858
|
if (this.currentContact && this.currentContact.status !== 'active') {
|
|
857
859
|
//no chatbox for archived, blocked, or stopped contacts
|
|
@@ -859,7 +861,7 @@ export class ContactChat extends ContactStoreElement {
|
|
|
859
861
|
} else {
|
|
860
862
|
if (!this.currentTicket.closed_on) {
|
|
861
863
|
//chatbox for active contacts with an open ticket
|
|
862
|
-
return this.
|
|
864
|
+
return this.getCompose();
|
|
863
865
|
} else {
|
|
864
866
|
return null;
|
|
865
867
|
}
|
|
@@ -871,13 +873,13 @@ export class ContactChat extends ContactStoreElement {
|
|
|
871
873
|
return null;
|
|
872
874
|
} else {
|
|
873
875
|
//chatbox for active contacts
|
|
874
|
-
return this.
|
|
876
|
+
return this.getCompose();
|
|
875
877
|
}
|
|
876
878
|
}
|
|
877
879
|
|
|
878
|
-
private
|
|
880
|
+
private getCompose(): TemplateResult {
|
|
879
881
|
return html`<div class="border"></div>
|
|
880
|
-
<div class="
|
|
882
|
+
<div class="compose">
|
|
881
883
|
<temba-compose
|
|
882
884
|
chatbox
|
|
883
885
|
attachments
|
|
@@ -231,7 +231,7 @@ export class ContactTickets extends EndpointMonitorElement {
|
|
|
231
231
|
): void {
|
|
232
232
|
super.updated(changes);
|
|
233
233
|
|
|
234
|
-
if (changes.has('data')) {
|
|
234
|
+
if (changes.has('data') && this.data) {
|
|
235
235
|
this.fireCustomEvent(CustomEventType.DetailsChanged, {
|
|
236
236
|
count: this.data.length
|
|
237
237
|
});
|
package/src/interfaces.ts
CHANGED
|
@@ -110,6 +110,13 @@ export interface ObjectReference {
|
|
|
110
110
|
name: string;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
export interface Shortcut {
|
|
114
|
+
uuid: string;
|
|
115
|
+
name: string;
|
|
116
|
+
text: string;
|
|
117
|
+
modified_on: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
113
120
|
export interface ContactField {
|
|
114
121
|
key: string;
|
|
115
122
|
label: string;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { css, html, PropertyValueMap, TemplateResult } from 'lit';
|
|
2
|
+
import { property } from 'lit/decorators.js';
|
|
3
|
+
import { Shortcut } from '../interfaces';
|
|
4
|
+
import { StoreMonitorElement } from '../store/StoreMonitorElement';
|
|
5
|
+
import { Options } from '../options/Options';
|
|
6
|
+
|
|
7
|
+
export class ShortcutList extends StoreMonitorElement {
|
|
8
|
+
static get styles() {
|
|
9
|
+
return css`
|
|
10
|
+
temba-options {
|
|
11
|
+
display: block;
|
|
12
|
+
width: 100%;
|
|
13
|
+
flex-grow: 1;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.options-empty {
|
|
17
|
+
height: 0;
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.no-match {
|
|
22
|
+
margin: 5px 10px;
|
|
23
|
+
padding: 10px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.filter {
|
|
27
|
+
background: #f3f3f3;
|
|
28
|
+
padding: 0.1em 0.3em;
|
|
29
|
+
border-radius: var(--curvature);
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@property({ type: String })
|
|
35
|
+
filter: string;
|
|
36
|
+
|
|
37
|
+
@property({ type: Array })
|
|
38
|
+
filteredShortcuts = [];
|
|
39
|
+
|
|
40
|
+
@property({ type: Number })
|
|
41
|
+
cursorIndex = 0;
|
|
42
|
+
|
|
43
|
+
constructor() {
|
|
44
|
+
super();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
protected firstUpdated(
|
|
48
|
+
changes: PropertyValueMap<any> | Map<PropertyKey, unknown>
|
|
49
|
+
): void {
|
|
50
|
+
super.firstUpdated(changes);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public storeUpdated(): void {
|
|
54
|
+
this.filteredShortcuts = this.store.getShortcuts();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public updated(
|
|
58
|
+
changes: PropertyValueMap<any> | Map<PropertyKey, unknown>
|
|
59
|
+
): void {
|
|
60
|
+
super.updated(changes);
|
|
61
|
+
|
|
62
|
+
if (changes.has('filter')) {
|
|
63
|
+
if (!this.filter) {
|
|
64
|
+
this.filteredShortcuts = this.store.getShortcuts();
|
|
65
|
+
} else {
|
|
66
|
+
this.filteredShortcuts = this.store
|
|
67
|
+
.getShortcuts()
|
|
68
|
+
.filter((shortcut: Shortcut) =>
|
|
69
|
+
shortcut.name.toLowerCase().includes(this.filter.toLowerCase())
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
this.cursorIndex = 0;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public renderShortcut(shortcut: Shortcut): TemplateResult {
|
|
77
|
+
return html`<div style="display:flex;align-items: center;min-width:0">
|
|
78
|
+
<div
|
|
79
|
+
style="
|
|
80
|
+
overflow: hidden;
|
|
81
|
+
text-overflow: ellipsis;
|
|
82
|
+
width:100px;
|
|
83
|
+
padding-right: 10px;
|
|
84
|
+
white-space: nowrap;"
|
|
85
|
+
>
|
|
86
|
+
${shortcut.name}
|
|
87
|
+
</div>
|
|
88
|
+
<div
|
|
89
|
+
style="
|
|
90
|
+
font-size: 0.9em;
|
|
91
|
+
color: rgba(0, 0, 0, 0.4);
|
|
92
|
+
flex:1;
|
|
93
|
+
overflow: hidden;
|
|
94
|
+
text-overflow: ellipsis;
|
|
95
|
+
|
|
96
|
+
display: -webkit-box;
|
|
97
|
+
line-height: 16px;
|
|
98
|
+
max-height: 16px;
|
|
99
|
+
-webkit-line-clamp: 1;
|
|
100
|
+
-webkit-box-orient: vertical;
|
|
101
|
+
"
|
|
102
|
+
>
|
|
103
|
+
${shortcut.text}
|
|
104
|
+
</div>
|
|
105
|
+
</div>`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public getShortcut() {
|
|
109
|
+
const options = this.shadowRoot.querySelector('temba-options') as Options;
|
|
110
|
+
return options.getSelection();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public render(): TemplateResult {
|
|
114
|
+
return html`
|
|
115
|
+
${this.filteredShortcuts.length === 0
|
|
116
|
+
? html`<div class="no-match">
|
|
117
|
+
${this.filter
|
|
118
|
+
? html`No matches for <span class="filter">${this.filter}</span>.`
|
|
119
|
+
: html`No shortcuts yet, create some to quickly include them in
|
|
120
|
+
your messages.`}
|
|
121
|
+
</div>`
|
|
122
|
+
: null}
|
|
123
|
+
|
|
124
|
+
<temba-options
|
|
125
|
+
class="options-${this.filteredShortcuts.length === 0
|
|
126
|
+
? 'empty'
|
|
127
|
+
: 'full'}"
|
|
128
|
+
block
|
|
129
|
+
cursorHover
|
|
130
|
+
visible
|
|
131
|
+
.renderOption=${this.renderShortcut}
|
|
132
|
+
.cursorIndex=${this.cursorIndex}
|
|
133
|
+
.options=${this.filteredShortcuts}
|
|
134
|
+
></temba-options>
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
}
|
package/src/list/TembaList.ts
CHANGED
|
@@ -410,9 +410,6 @@ export class TembaList extends RapidElement {
|
|
|
410
410
|
}
|
|
411
411
|
}
|
|
412
412
|
|
|
413
|
-
// TODO: Not sure why this is needed
|
|
414
|
-
// this.requestUpdate('cursorIndex');
|
|
415
|
-
|
|
416
413
|
if (this.value) {
|
|
417
414
|
this.setSelection(this.value);
|
|
418
415
|
this.value = null;
|
|
@@ -456,7 +453,12 @@ export class TembaList extends RapidElement {
|
|
|
456
453
|
}
|
|
457
454
|
|
|
458
455
|
protected handleSelection(event: CustomEvent) {
|
|
459
|
-
|
|
456
|
+
let index = event.detail.index;
|
|
457
|
+
const selected = event.detail.selected;
|
|
458
|
+
|
|
459
|
+
if (index === -1) {
|
|
460
|
+
index = 0;
|
|
461
|
+
}
|
|
460
462
|
|
|
461
463
|
this.selected = selected;
|
|
462
464
|
this.cursorIndex = index;
|
|
@@ -470,8 +472,9 @@ export class TembaList extends RapidElement {
|
|
|
470
472
|
${this.renderHeader()}
|
|
471
473
|
<temba-options
|
|
472
474
|
style="${this.getListStyle()}"
|
|
473
|
-
|
|
474
|
-
|
|
475
|
+
visible
|
|
476
|
+
block
|
|
477
|
+
cursorSelection
|
|
475
478
|
?hideShadow=${this.hideShadow}
|
|
476
479
|
?collapsed=${this.collapsed}
|
|
477
480
|
?loading=${this.loading}
|
package/src/options/Options.ts
CHANGED
|
@@ -111,6 +111,7 @@ export class Options extends RapidElement {
|
|
|
111
111
|
margin: 0.3em;
|
|
112
112
|
cursor: pointer;
|
|
113
113
|
color: var(--color-text-dark);
|
|
114
|
+
scroll-margin: 5px 0px;
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
.option * {
|
|
@@ -209,6 +210,14 @@ export class Options extends RapidElement {
|
|
|
209
210
|
@property({ type: Boolean })
|
|
210
211
|
block: boolean;
|
|
211
212
|
|
|
213
|
+
// if we allow the focus to follow the cursor
|
|
214
|
+
@property({ type: Boolean })
|
|
215
|
+
cursorHover: boolean;
|
|
216
|
+
|
|
217
|
+
// fire selection events when cursor changes
|
|
218
|
+
@property({ type: Boolean })
|
|
219
|
+
cursorSelection: boolean;
|
|
220
|
+
|
|
212
221
|
@property({ type: Number })
|
|
213
222
|
scrollPct = 75;
|
|
214
223
|
|
|
@@ -302,67 +311,42 @@ export class Options extends RapidElement {
|
|
|
302
311
|
return focused;
|
|
303
312
|
}
|
|
304
313
|
|
|
305
|
-
public updated(
|
|
306
|
-
super.updated(
|
|
314
|
+
public updated(changed: Map<string, any>) {
|
|
315
|
+
super.updated(changed);
|
|
307
316
|
|
|
308
317
|
// if our cursor changed, lets make sure our scrollbox is showing it
|
|
309
|
-
if (!this.internalFocusDisabled &&
|
|
310
|
-
const focusedOption = this.shadowRoot.querySelector(
|
|
311
|
-
`div[data-option-index="${this.cursorIndex}"]`
|
|
312
|
-
) as HTMLDivElement;
|
|
313
|
-
|
|
314
|
-
if (focusedOption) {
|
|
315
|
-
const scrollBox = this.shadowRoot.querySelector('.options-container');
|
|
316
|
-
const scrollBoxRect = scrollBox.getBoundingClientRect();
|
|
317
|
-
const scrollBoxHeight = scrollBoxRect.height;
|
|
318
|
-
const focusedEleHeight = focusedOption.getBoundingClientRect().height;
|
|
319
|
-
|
|
320
|
-
if (
|
|
321
|
-
focusedOption.offsetTop + focusedEleHeight >
|
|
322
|
-
scrollBox.scrollTop + scrollBoxHeight - 5
|
|
323
|
-
) {
|
|
324
|
-
const scrollTo =
|
|
325
|
-
focusedOption.offsetTop - scrollBoxHeight + focusedEleHeight + 5;
|
|
326
|
-
scrollBox.scrollTop = scrollTo;
|
|
327
|
-
} else if (focusedOption.offsetTop < scrollBox.scrollTop) {
|
|
328
|
-
const scrollTo = focusedOption.offsetTop - 5;
|
|
329
|
-
scrollBox.scrollTop = scrollTo;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
318
|
+
if (!this.internalFocusDisabled && changed.has('cursorIndex')) {
|
|
333
319
|
this.fireCustomEvent(CustomEventType.CursorChanged, {
|
|
334
320
|
index: this.cursorIndex
|
|
335
321
|
});
|
|
336
322
|
}
|
|
337
323
|
|
|
338
|
-
if (
|
|
324
|
+
if (changed.has('visible') && changed.has('options')) {
|
|
339
325
|
if (!this.visible && this.options.length == 0) {
|
|
340
|
-
this.tempOptions =
|
|
326
|
+
this.tempOptions = changed.get('options');
|
|
341
327
|
window.setTimeout(() => {
|
|
342
328
|
this.tempOptions = [];
|
|
343
329
|
}, 300);
|
|
344
330
|
}
|
|
345
331
|
}
|
|
346
332
|
|
|
347
|
-
if (
|
|
333
|
+
if (changed.has('options')) {
|
|
348
334
|
this.calculatePosition();
|
|
349
335
|
|
|
350
336
|
// allow scrolls to trigger again
|
|
351
337
|
this.triggerScroll = true;
|
|
352
338
|
|
|
353
|
-
const prevOptions =
|
|
339
|
+
const prevOptions = changed.get('options');
|
|
354
340
|
const previousCount = prevOptions ? prevOptions.length : 0;
|
|
355
341
|
const newCount = this.options ? this.options.length : 0;
|
|
356
342
|
|
|
357
343
|
if (
|
|
358
344
|
this.cursorIndex === -1 ||
|
|
359
345
|
newCount < previousCount ||
|
|
360
|
-
(previousCount === 0 &&
|
|
361
|
-
newCount > 0 &&
|
|
362
|
-
!changedProperties.has('cursorIndex'))
|
|
346
|
+
(previousCount === 0 && newCount > 0 && !changed.has('cursorIndex'))
|
|
363
347
|
) {
|
|
364
348
|
if (!this.internalFocusDisabled) {
|
|
365
|
-
if (!this.block) {
|
|
349
|
+
if (!this.block || this.cursorIndex === -1) {
|
|
366
350
|
this.cursorIndex = 0;
|
|
367
351
|
} else {
|
|
368
352
|
if (this.cursorIndex >= newCount) {
|
|
@@ -370,7 +354,7 @@ export class Options extends RapidElement {
|
|
|
370
354
|
}
|
|
371
355
|
}
|
|
372
356
|
|
|
373
|
-
if (this.
|
|
357
|
+
if (this.cursorSelection) {
|
|
374
358
|
this.handleSelection(false);
|
|
375
359
|
}
|
|
376
360
|
}
|
|
@@ -379,12 +363,14 @@ export class Options extends RapidElement {
|
|
|
379
363
|
// if on initial load we don't have enough options to load, trigger a scroll
|
|
380
364
|
// threshold event in case the page size is smaller than our control height
|
|
381
365
|
const scrollBox = this.shadowRoot.querySelector('.options');
|
|
382
|
-
if (scrollBox
|
|
383
|
-
|
|
366
|
+
if (scrollBox) {
|
|
367
|
+
if (scrollBox.scrollHeight == scrollBox.clientHeight) {
|
|
368
|
+
this.fireCustomEvent(CustomEventType.ScrollThreshold);
|
|
369
|
+
}
|
|
384
370
|
}
|
|
385
371
|
}
|
|
386
372
|
|
|
387
|
-
if (
|
|
373
|
+
if (changed.has('visible')) {
|
|
388
374
|
window.setTimeout(() => {
|
|
389
375
|
this.calculatePosition();
|
|
390
376
|
}, 100);
|
|
@@ -427,6 +413,10 @@ export class Options extends RapidElement {
|
|
|
427
413
|
return html` ${option.detail} `;
|
|
428
414
|
}
|
|
429
415
|
|
|
416
|
+
public getSelection() {
|
|
417
|
+
return this.options[this.cursorIndex];
|
|
418
|
+
}
|
|
419
|
+
|
|
430
420
|
private handleSelection(tabbed = false, index = -1) {
|
|
431
421
|
if (!this.internalFocusDisabled) {
|
|
432
422
|
if (index === -1) {
|
|
@@ -468,25 +458,42 @@ export class Options extends RapidElement {
|
|
|
468
458
|
scrollBox.scrollTop = 0;
|
|
469
459
|
}
|
|
470
460
|
|
|
461
|
+
private ensureOptionVisible() {
|
|
462
|
+
const focusedOption = this.shadowRoot.querySelector(
|
|
463
|
+
`div[data-option-index="${this.cursorIndex}"]`
|
|
464
|
+
) as HTMLDivElement;
|
|
465
|
+
if (focusedOption) {
|
|
466
|
+
focusedOption.scrollIntoView({
|
|
467
|
+
block: 'nearest',
|
|
468
|
+
inline: 'start'
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
}
|
|
471
472
|
private handleKeyDown(evt: KeyboardEvent) {
|
|
472
473
|
if (this.internalFocusDisabled || (this.block && !this.isFocused())) {
|
|
473
474
|
return;
|
|
474
475
|
}
|
|
475
476
|
|
|
477
|
+
if (this.offsetParent === null) {
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
|
|
476
481
|
if (this.options && this.options.length > 0) {
|
|
477
482
|
if ((evt.ctrlKey && evt.key === 'n') || evt.key === 'ArrowDown') {
|
|
478
483
|
this.moveCursor(1);
|
|
479
484
|
evt.preventDefault();
|
|
480
485
|
evt.stopPropagation();
|
|
481
|
-
if (this.
|
|
486
|
+
if (this.cursorSelection) {
|
|
482
487
|
this.handleSelection(false);
|
|
483
488
|
}
|
|
489
|
+
this.ensureOptionVisible();
|
|
484
490
|
} else if ((evt.ctrlKey && evt.key === 'p') || evt.key === 'ArrowUp') {
|
|
485
491
|
this.moveCursor(-1);
|
|
486
492
|
evt.preventDefault();
|
|
487
|
-
if (this.
|
|
493
|
+
if (this.cursorSelection) {
|
|
488
494
|
this.handleSelection(false);
|
|
489
495
|
}
|
|
496
|
+
this.ensureOptionVisible();
|
|
490
497
|
} else if (
|
|
491
498
|
evt.key === 'Enter' ||
|
|
492
499
|
evt.key === 'Tab' ||
|
|
@@ -495,9 +502,7 @@ export class Options extends RapidElement {
|
|
|
495
502
|
evt.preventDefault();
|
|
496
503
|
evt.stopPropagation();
|
|
497
504
|
this.handleSelection(evt.key === 'Tab');
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
if (evt.key === 'Escape') {
|
|
505
|
+
} else if (evt.key === 'Escape') {
|
|
501
506
|
this.fireCustomEvent(CustomEventType.Canceled);
|
|
502
507
|
}
|
|
503
508
|
}
|
|
@@ -578,7 +583,7 @@ export class Options extends RapidElement {
|
|
|
578
583
|
}
|
|
579
584
|
|
|
580
585
|
private handleMouseMove(evt: MouseEvent) {
|
|
581
|
-
if (!this.block) {
|
|
586
|
+
if (!this.block || this.cursorHover) {
|
|
582
587
|
if (Math.abs(evt.movementX) + Math.abs(evt.movementY) > 0) {
|
|
583
588
|
const index = (evt.currentTarget as HTMLElement).getAttribute(
|
|
584
589
|
'data-option-index'
|
|
@@ -610,6 +615,10 @@ export class Options extends RapidElement {
|
|
|
610
615
|
}
|
|
611
616
|
|
|
612
617
|
public render(): TemplateResult {
|
|
618
|
+
if (!this.resolvedRenderOption) {
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
|
|
613
622
|
let vertical = this.block ? 0 : this.marginVertical;
|
|
614
623
|
if (this.poppedTop) {
|
|
615
624
|
vertical *= -1;
|
package/src/select/Select.ts
CHANGED
package/src/store/Store.ts
CHANGED
|
@@ -18,7 +18,8 @@ import {
|
|
|
18
18
|
KeyedAssets,
|
|
19
19
|
CustomEventType,
|
|
20
20
|
Workspace,
|
|
21
|
-
User
|
|
21
|
+
User,
|
|
22
|
+
Shortcut
|
|
22
23
|
} from '../interfaces';
|
|
23
24
|
import { RapidElement } from '../RapidElement';
|
|
24
25
|
import { lru } from 'tiny-lru';
|
|
@@ -85,6 +86,9 @@ export class Store extends RapidElement {
|
|
|
85
86
|
@property({ type: String, attribute: 'users' })
|
|
86
87
|
usersEndpoint: string;
|
|
87
88
|
|
|
89
|
+
@property({ type: String, attribute: 'shortcuts' })
|
|
90
|
+
shortcutsEndpoint: string;
|
|
91
|
+
|
|
88
92
|
@property({ type: Object, attribute: false })
|
|
89
93
|
private schema: CompletionSchema;
|
|
90
94
|
|
|
@@ -98,6 +102,7 @@ export class Store extends RapidElement {
|
|
|
98
102
|
|
|
99
103
|
private fields: { [key: string]: ContactField } = {};
|
|
100
104
|
private groups: { [uuid: string]: ContactGroup } = {};
|
|
105
|
+
private shortcuts: Shortcut[] = [];
|
|
101
106
|
private languages: any = {};
|
|
102
107
|
private users: User[];
|
|
103
108
|
private workspace: Workspace;
|
|
@@ -229,6 +234,10 @@ export class Store extends RapidElement {
|
|
|
229
234
|
);
|
|
230
235
|
}
|
|
231
236
|
|
|
237
|
+
if (this.shortcutsEndpoint) {
|
|
238
|
+
fetches.push(this.refreshShortcuts());
|
|
239
|
+
}
|
|
240
|
+
|
|
232
241
|
this.initialHttpComplete = Promise.all(fetches);
|
|
233
242
|
|
|
234
243
|
this.initialHttpComplete.then(() => {
|
|
@@ -236,6 +245,10 @@ export class Store extends RapidElement {
|
|
|
236
245
|
});
|
|
237
246
|
}
|
|
238
247
|
|
|
248
|
+
public getShortcuts() {
|
|
249
|
+
return this.shortcuts || [];
|
|
250
|
+
}
|
|
251
|
+
|
|
239
252
|
public getAssignableUsers() {
|
|
240
253
|
return this.users.filter((user: User) =>
|
|
241
254
|
['administrator', 'editor', 'agent'].includes(user.role)
|
|
@@ -257,13 +270,19 @@ export class Store extends RapidElement {
|
|
|
257
270
|
return 'en';
|
|
258
271
|
}
|
|
259
272
|
|
|
260
|
-
public refreshGlobals() {
|
|
261
|
-
getAssets(this.globalsEndpoint).then((assets: Asset[]) => {
|
|
273
|
+
public async refreshGlobals() {
|
|
274
|
+
return getAssets(this.globalsEndpoint).then((assets: Asset[]) => {
|
|
262
275
|
this.keyedAssets['globals'] = assets.map((asset: Asset) => asset.key);
|
|
263
276
|
});
|
|
264
277
|
}
|
|
265
278
|
|
|
266
|
-
public
|
|
279
|
+
public async refreshShortcuts() {
|
|
280
|
+
return getAssets(this.shortcutsEndpoint).then((shortcuts: Shortcut[]) => {
|
|
281
|
+
this.shortcuts = shortcuts;
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
public async refreshFields() {
|
|
267
286
|
return getAssets(this.fieldsEndpoint).then((assets: Asset[]) => {
|
|
268
287
|
this.keyedAssets['fields'] = [];
|
|
269
288
|
this.featuredFields = [];
|
package/src/tabpane/Tab.ts
CHANGED
package/src/tabpane/TabPane.ts
CHANGED
|
@@ -82,7 +82,6 @@ export class TabPane extends RapidElement {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
.focusedname .tab.selected {
|
|
85
|
-
transform: none;
|
|
86
85
|
}
|
|
87
86
|
|
|
88
87
|
.focusedname .tab .name {
|
|
@@ -119,8 +118,6 @@ export class TabPane extends RapidElement {
|
|
|
119
118
|
}
|
|
120
119
|
|
|
121
120
|
.embedded .tab.selected {
|
|
122
|
-
border: none;
|
|
123
|
-
transform: none;
|
|
124
121
|
}
|
|
125
122
|
|
|
126
123
|
.tab.selected .dot {
|
|
@@ -153,7 +150,10 @@ export class TabPane extends RapidElement {
|
|
|
153
150
|
flex-direction: column;
|
|
154
151
|
flex-grow: 1;
|
|
155
152
|
background: var(--focused-tab-color, #fff);
|
|
156
|
-
border-radius: var(--curvature);
|
|
153
|
+
border-bottom-left-radius: var(--curvature);
|
|
154
|
+
border-bottom-right-radius: var(--curvature);
|
|
155
|
+
overflow: hidden;
|
|
156
|
+
|
|
157
157
|
box-shadow: var(
|
|
158
158
|
--tabs-shadow,
|
|
159
159
|
rgba(0, 0, 0, 0.1) 0px 1px 3px 0px,
|
|
@@ -217,6 +217,9 @@ export class TabPane extends RapidElement {
|
|
|
217
217
|
.embedded.pane {
|
|
218
218
|
box-shadow: none;
|
|
219
219
|
margin: 0;
|
|
220
|
+
border-left: none !important;
|
|
221
|
+
border-right: none !important;
|
|
222
|
+
border-bottom: none !important;
|
|
220
223
|
}
|
|
221
224
|
|
|
222
225
|
.embedded.tabs {
|
|
@@ -224,6 +227,13 @@ export class TabPane extends RapidElement {
|
|
|
224
227
|
}
|
|
225
228
|
|
|
226
229
|
.embedded .tab {
|
|
230
|
+
border-top: none !important;
|
|
231
|
+
border-bottom: none !important;
|
|
232
|
+
border-radius: 0px;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.embedded .tab.first {
|
|
236
|
+
border-left: none !important;
|
|
227
237
|
}
|
|
228
238
|
|
|
229
239
|
.embedded.tabs .tab.selected {
|
|
@@ -332,6 +342,14 @@ export class TabPane extends RapidElement {
|
|
|
332
342
|
}
|
|
333
343
|
}
|
|
334
344
|
|
|
345
|
+
public focusTab(name: string): Tab {
|
|
346
|
+
const index = this.tabs.findIndex((tab) => tab.name === name);
|
|
347
|
+
if (index >= 0) {
|
|
348
|
+
this.index = index;
|
|
349
|
+
return this.getTab(index);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
335
353
|
public setTabDetails(
|
|
336
354
|
index: number,
|
|
337
355
|
details: { count: number; hidden: boolean }
|
|
@@ -444,7 +462,11 @@ export class TabPane extends RapidElement {
|
|
|
444
462
|
${!this.bottom
|
|
445
463
|
? html`<div
|
|
446
464
|
@temba-details-changed=${this.handleTabDetailsChanged}
|
|
447
|
-
style="
|
|
465
|
+
style="${activeTab?.borderColor
|
|
466
|
+
? `border: 1px solid ${activeTab?.borderColor};`
|
|
467
|
+
: ''} ${activeTab?.selectionBackground
|
|
468
|
+
? `background:${activeTab?.selectionBackground};`
|
|
469
|
+
: ``}"
|
|
448
470
|
class="pane ${getClasses({
|
|
449
471
|
first: this.index == 0,
|
|
450
472
|
embedded: this.embedded,
|
|
@@ -452,6 +474,7 @@ export class TabPane extends RapidElement {
|
|
|
452
474
|
})}"
|
|
453
475
|
>
|
|
454
476
|
<slot></slot>
|
|
477
|
+
<slot name="pane-bottom"></slot>
|
|
455
478
|
</div>`
|
|
456
479
|
: null}
|
|
457
480
|
`;
|