@nyaruka/temba-components 0.171.0 → 0.172.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 +23 -1
- package/dist/temba-components.js +1015 -747
- package/dist/temba-components.js.map +1 -1
- package/package.json +1 -1
- package/src/RapidElement.ts +83 -41
- package/src/display/Button.ts +14 -2
- package/src/display/Chat.ts +41 -22
- package/src/display/Label.ts +18 -3
- package/src/events/eventRenderers.ts +4 -0
- package/src/flow/FlowSearch.ts +12 -392
- package/src/flow/actions/send_msg.ts +2 -1
- package/src/flow/actions/set_contact_field.ts +1 -0
- package/src/flow/actions/set_contact_name.ts +1 -0
- package/src/flow/nodes/split_by_ticket.ts +1 -0
- package/src/flow/nodes/wait_for_dial.ts +1 -0
- package/src/form/RangePicker.ts +10 -2
- package/src/form/select/Select.ts +15 -2
- package/src/layout/SearchModal.ts +564 -0
- package/src/list/BroadcastList.ts +17 -6
- package/src/list/ContentList.ts +29 -13
- package/src/list/FieldList.ts +8 -1
- package/src/list/TembaMenu.ts +71 -13
- package/src/live/CampaignEvents.ts +31 -11
- package/src/live/ContactChat.ts +276 -76
- package/src/live/ContactTimeline.ts +46 -24
- package/src/live/ContactWatch.ts +166 -63
- package/src/live/Realtime.ts +137 -8
- package/src/live/SocketService.ts +115 -10
- package/src/live/TicketSearch.ts +290 -0
- package/src/live/Watchers.ts +93 -0
- package/src/simulator/Simulator.ts +59 -36
- package/src/store/Store.ts +17 -34
- package/src/styles/designTokens.ts +57 -17
- package/src/styles/pillVariants.ts +42 -10
- package/static/css/design-system.css +47 -12
- package/static/css/temba-components.css +31 -9
- package/temba-modules.ts +2 -0
package/package.json
CHANGED
package/src/RapidElement.ts
CHANGED
|
@@ -40,6 +40,34 @@ export interface EventHandler {
|
|
|
40
40
|
isWindow?: boolean;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Inline handlers compiled from an element's -<event-type> attribute, keyed
|
|
45
|
+
* by their source. Page authors write a finite set of these, so compiling
|
|
46
|
+
* each one once keeps dispatch off the Function constructor entirely.
|
|
47
|
+
*/
|
|
48
|
+
const inlineHandlers = new Map<string, (event: Event) => any>();
|
|
49
|
+
|
|
50
|
+
const compileInlineHandler = (source: string): ((event: Event) => any) => {
|
|
51
|
+
let compiled = inlineHandlers.get(source);
|
|
52
|
+
if (!compiled) {
|
|
53
|
+
compiled = new Function(
|
|
54
|
+
'event',
|
|
55
|
+
`
|
|
56
|
+
with(document) {
|
|
57
|
+
with(this) {
|
|
58
|
+
let handler = ${source};
|
|
59
|
+
if(typeof handler === 'function') {
|
|
60
|
+
handler(event);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
`
|
|
65
|
+
) as (event: Event) => any;
|
|
66
|
+
inlineHandlers.set(source, compiled);
|
|
67
|
+
}
|
|
68
|
+
return compiled;
|
|
69
|
+
};
|
|
70
|
+
|
|
43
71
|
export class RapidElement extends LitElement {
|
|
44
72
|
DEBUG = false;
|
|
45
73
|
DEBUG_UPDATES = false;
|
|
@@ -49,34 +77,50 @@ export class RapidElement extends LitElement {
|
|
|
49
77
|
service: string;
|
|
50
78
|
|
|
51
79
|
private eles: { [selector: string]: HTMLDivElement } = {};
|
|
80
|
+
|
|
81
|
+
// teardowns for the listeners we installed, so disconnecting removes the
|
|
82
|
+
// exact functions we added
|
|
83
|
+
private listenerTeardowns: (() => void)[] = [];
|
|
84
|
+
|
|
52
85
|
public getEventHandlers(): EventHandler[] {
|
|
53
86
|
return [];
|
|
54
87
|
}
|
|
55
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Adds a listener this element owns the teardown for - it is bound to this
|
|
91
|
+
* element and removed automatically when we disconnect. Use this instead of
|
|
92
|
+
* addEventListener for anything on document or window, where a listener
|
|
93
|
+
* that outlives its element keeps the whole element alive.
|
|
94
|
+
*/
|
|
95
|
+
public listenTo(
|
|
96
|
+
target: EventTarget,
|
|
97
|
+
event: string,
|
|
98
|
+
method: EventListener,
|
|
99
|
+
options?: AddEventListenerOptions
|
|
100
|
+
): void {
|
|
101
|
+
const bound = method.bind(this);
|
|
102
|
+
target.addEventListener(event, bound, options);
|
|
103
|
+
this.listenerTeardowns.push(() =>
|
|
104
|
+
target.removeEventListener(event, bound, options)
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
56
108
|
connectedCallback() {
|
|
57
109
|
super.connectedCallback();
|
|
58
110
|
|
|
59
111
|
for (const handler of this.getEventHandlers()) {
|
|
60
|
-
|
|
61
|
-
document
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
112
|
+
const target = handler.isDocument
|
|
113
|
+
? document
|
|
114
|
+
: handler.isWindow
|
|
115
|
+
? window
|
|
116
|
+
: this;
|
|
117
|
+
this.listenTo(target, handler.event, handler.method);
|
|
67
118
|
}
|
|
68
119
|
}
|
|
69
120
|
|
|
70
121
|
disconnectedCallback() {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
document.removeEventListener(handler.event, handler.method);
|
|
74
|
-
} else if (handler.isWindow) {
|
|
75
|
-
window.removeEventListener(handler.event, handler.method);
|
|
76
|
-
} else {
|
|
77
|
-
this.removeEventListener(handler.event, handler.method);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
122
|
+
this.listenerTeardowns.forEach((teardown) => teardown());
|
|
123
|
+
this.listenerTeardowns = [];
|
|
80
124
|
super.disconnectedCallback();
|
|
81
125
|
}
|
|
82
126
|
|
|
@@ -121,9 +165,7 @@ export class RapidElement extends LitElement {
|
|
|
121
165
|
}
|
|
122
166
|
|
|
123
167
|
public fireCustomEvent(type: CustomEventType, detail: any = {}): any {
|
|
124
|
-
|
|
125
|
-
showEvent(this, type, detail);
|
|
126
|
-
}
|
|
168
|
+
showEvent(this, type, detail);
|
|
127
169
|
|
|
128
170
|
const event = new CustomEvent(type, {
|
|
129
171
|
detail,
|
|
@@ -135,30 +177,30 @@ export class RapidElement extends LitElement {
|
|
|
135
177
|
}
|
|
136
178
|
|
|
137
179
|
public dispatchEvent(event: any): any {
|
|
138
|
-
super.dispatchEvent(event);
|
|
180
|
+
const dispatched = super.dispatchEvent(event);
|
|
181
|
+
|
|
182
|
+
// the page can hang a handler off the target for any of our events, as a
|
|
183
|
+
// -<event-type> property or an attribute of the same name
|
|
139
184
|
const ele = event.target;
|
|
140
|
-
if (ele) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
'event',
|
|
148
|
-
`
|
|
149
|
-
with(document) {
|
|
150
|
-
with(this) {
|
|
151
|
-
let handler = ${ele.getAttribute('-' + event.type)};
|
|
152
|
-
if(typeof handler === 'function') {
|
|
153
|
-
handler(event);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
`
|
|
158
|
-
);
|
|
159
|
-
return func.call(ele, event);
|
|
160
|
-
}
|
|
185
|
+
if (!ele) {
|
|
186
|
+
return dispatched;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const eventFire = (ele as any)['-' + event.type];
|
|
190
|
+
if (eventFire) {
|
|
191
|
+
return eventFire(event);
|
|
161
192
|
}
|
|
193
|
+
|
|
194
|
+
const inline = ele.getAttribute ? ele.getAttribute('-' + event.type) : null;
|
|
195
|
+
if (!inline) {
|
|
196
|
+
return dispatched;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// the compiled handler has no return value of its own, so handing its
|
|
200
|
+
// undefined back would read as a cancelled event - what the caller wants
|
|
201
|
+
// to know is whether anything called preventDefault
|
|
202
|
+
compileInlineHandler(inline).call(ele, event);
|
|
203
|
+
return dispatched;
|
|
162
204
|
}
|
|
163
205
|
|
|
164
206
|
public closestElement(selector: string, base: Element = this) {
|
package/src/display/Button.ts
CHANGED
|
@@ -101,9 +101,11 @@ export class Button extends LitElement {
|
|
|
101
101
|
background: var(--success, #16a34a);
|
|
102
102
|
color: #fff;
|
|
103
103
|
}
|
|
104
|
+
/* Hover darkening: static pre-mixed values first for browsers
|
|
105
|
+
without color-mix(), re-derived from the token below. */
|
|
104
106
|
.attention-button:hover,
|
|
105
107
|
.affirmative:hover {
|
|
106
|
-
background:
|
|
108
|
+
background: #138f41;
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
/* DS .btn-danger — flat danger fill (no lift). */
|
|
@@ -112,7 +114,17 @@ export class Button extends LitElement {
|
|
|
112
114
|
color: #fff;
|
|
113
115
|
}
|
|
114
116
|
.destructive-button:hover {
|
|
115
|
-
background:
|
|
117
|
+
background: #b73737;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@supports (color: color-mix(in srgb, red, red)) {
|
|
121
|
+
.attention-button:hover,
|
|
122
|
+
.affirmative:hover {
|
|
123
|
+
background: color-mix(in srgb, var(--success, #16a34a) 88%, black);
|
|
124
|
+
}
|
|
125
|
+
.destructive-button:hover {
|
|
126
|
+
background: color-mix(in srgb, var(--danger, #d03f3f) 88%, black);
|
|
127
|
+
}
|
|
116
128
|
}
|
|
117
129
|
|
|
118
130
|
/* DS .btn-ghost — text-only, hover fills with sunken */
|
package/src/display/Chat.ts
CHANGED
|
@@ -518,19 +518,15 @@ export class Chat extends RapidElement {
|
|
|
518
518
|
same theme vars the flat fills used — color-mix keeps host
|
|
519
519
|
re-theming working — with a whisper of shadow for depth and
|
|
520
520
|
a hairline border mixed just darker than the fill so bubbles
|
|
521
|
-
hold their edge against the background.
|
|
521
|
+
hold their edge against the background. Base rules carry flat
|
|
522
|
+
fills and pre-mixed borders for browsers without color-mix()
|
|
523
|
+
(pre-Chrome 111); the @supports block below layers the
|
|
524
|
+
gradients on top. */
|
|
522
525
|
.bubble {
|
|
523
526
|
padding: 10px 14px;
|
|
524
|
-
background:
|
|
525
|
-
180deg,
|
|
526
|
-
color-mix(in srgb, var(--color-chat-in, #e5e5ea) 62%, white) 0%,
|
|
527
|
-
var(--color-chat-in, #e5e5ea) 100%
|
|
528
|
-
);
|
|
527
|
+
background: var(--color-chat-in, #e5e5ea);
|
|
529
528
|
border-radius: 18px;
|
|
530
|
-
border: var(
|
|
531
|
-
--chat-border-in,
|
|
532
|
-
1px solid color-mix(in srgb, var(--color-chat-in, #e5e5ea) 93%, black)
|
|
533
|
-
);
|
|
529
|
+
border: var(--chat-border-in, 1px solid #d5d5da);
|
|
534
530
|
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.06);
|
|
535
531
|
}
|
|
536
532
|
|
|
@@ -550,20 +546,40 @@ export class Chat extends RapidElement {
|
|
|
550
546
|
}
|
|
551
547
|
|
|
552
548
|
.incoming .bubble {
|
|
553
|
-
background:
|
|
554
|
-
|
|
555
|
-
color-mix(in srgb, var(--color-chat-out, #007aff) 90%, white) 0%,
|
|
556
|
-
var(--color-chat-out, #007aff) 58%,
|
|
557
|
-
color-mix(in srgb, var(--color-chat-out, #007aff) 95%, black) 100%
|
|
558
|
-
);
|
|
559
|
-
border: var(
|
|
560
|
-
--chat-border-out,
|
|
561
|
-
1px solid
|
|
562
|
-
color-mix(in srgb, var(--color-chat-out, #007aff) 90%, black)
|
|
563
|
-
);
|
|
549
|
+
background: var(--color-chat-out, #007aff);
|
|
550
|
+
border: var(--chat-border-out, 1px solid #006ee6);
|
|
564
551
|
color: white;
|
|
565
552
|
}
|
|
566
553
|
|
|
554
|
+
@supports (color: color-mix(in srgb, red, red)) {
|
|
555
|
+
.bubble {
|
|
556
|
+
background: linear-gradient(
|
|
557
|
+
180deg,
|
|
558
|
+
color-mix(in srgb, var(--color-chat-in, #e5e5ea) 62%, white) 0%,
|
|
559
|
+
var(--color-chat-in, #e5e5ea) 100%
|
|
560
|
+
);
|
|
561
|
+
border: var(
|
|
562
|
+
--chat-border-in,
|
|
563
|
+
1px solid
|
|
564
|
+
color-mix(in srgb, var(--color-chat-in, #e5e5ea) 93%, black)
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.incoming .bubble {
|
|
569
|
+
background: linear-gradient(
|
|
570
|
+
180deg,
|
|
571
|
+
color-mix(in srgb, var(--color-chat-out, #007aff) 90%, white) 0%,
|
|
572
|
+
var(--color-chat-out, #007aff) 58%,
|
|
573
|
+
color-mix(in srgb, var(--color-chat-out, #007aff) 95%, black) 100%
|
|
574
|
+
);
|
|
575
|
+
border: var(
|
|
576
|
+
--chat-border-out,
|
|
577
|
+
1px solid
|
|
578
|
+
color-mix(in srgb, var(--color-chat-out, #007aff) 90%, black)
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
567
583
|
.incoming .latest .bubble {
|
|
568
584
|
border-bottom-right-radius: 4px;
|
|
569
585
|
}
|
|
@@ -573,8 +589,11 @@ export class Chat extends RapidElement {
|
|
|
573
589
|
}
|
|
574
590
|
|
|
575
591
|
/* Notes get the same top-lit gradient treatment as message
|
|
576
|
-
bubbles, derived from their sticky-note yellow.
|
|
592
|
+
bubbles, derived from their sticky-note yellow. The flat fill
|
|
593
|
+
first is the fallback for browsers without color-mix() — all
|
|
594
|
+
literals here, so the invalid gradient drops at parse time. */
|
|
577
595
|
.note .bubble {
|
|
596
|
+
background: #fffac3;
|
|
578
597
|
background: linear-gradient(
|
|
579
598
|
180deg,
|
|
580
599
|
color-mix(in srgb, #fffac3 62%, white) 0%,
|
package/src/display/Label.ts
CHANGED
|
@@ -100,7 +100,10 @@ export default class Label extends LitElement {
|
|
|
100
100
|
dark variant shade), so flow stays bluish, group stays purplish,
|
|
101
101
|
etc. — no grey wash. */
|
|
102
102
|
.label[class*='pill-'].clickable .mask:hover {
|
|
103
|
-
|
|
103
|
+
/* Neutral wash first for browsers without color-mix() — the
|
|
104
|
+
currentColor mix can't fall back per-declaration because
|
|
105
|
+
currentColor keeps it valid until computed-value time. */
|
|
106
|
+
background: rgba(0, 0, 0, 0.06);
|
|
104
107
|
}
|
|
105
108
|
.label[class*='pill-'] temba-icon {
|
|
106
109
|
margin-right: 0;
|
|
@@ -121,14 +124,26 @@ export default class Label extends LitElement {
|
|
|
121
124
|
margin: 0;
|
|
122
125
|
border: 0;
|
|
123
126
|
border-radius: 999px;
|
|
124
|
-
background:
|
|
127
|
+
background: rgba(0, 0, 0, 0.12);
|
|
125
128
|
color: inherit;
|
|
126
129
|
opacity: 0.8;
|
|
127
130
|
--icon-color: currentColor;
|
|
128
131
|
}
|
|
129
132
|
.label[class*='pill-'] .remove:hover {
|
|
130
133
|
opacity: 1;
|
|
131
|
-
background:
|
|
134
|
+
background: rgba(0, 0, 0, 0.22);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@supports (color: color-mix(in srgb, red, red)) {
|
|
138
|
+
.label[class*='pill-'].clickable .mask:hover {
|
|
139
|
+
background: color-mix(in oklab, currentColor 10%, transparent);
|
|
140
|
+
}
|
|
141
|
+
.label[class*='pill-'] .remove {
|
|
142
|
+
background: color-mix(in oklab, currentColor 25%, transparent);
|
|
143
|
+
}
|
|
144
|
+
.label[class*='pill-'] .remove:hover {
|
|
145
|
+
background: color-mix(in oklab, currentColor 45%, transparent);
|
|
146
|
+
}
|
|
132
147
|
}
|
|
133
148
|
|
|
134
149
|
/* When a removable X is present, tighten the mask's left padding
|
|
@@ -59,6 +59,10 @@ export enum Events {
|
|
|
59
59
|
TICKET_OPENED = 'ticket_opened',
|
|
60
60
|
TICKET_REOPENED = 'ticket_reopened',
|
|
61
61
|
TICKET_TOPIC_CHANGED = 'ticket_topic_changed',
|
|
62
|
+
// ephemeral, drives the chat's typing indicator instead of recording
|
|
63
|
+
// history - published by agents as they compose and never persisted
|
|
64
|
+
TYPING_STARTED = 'typing_started',
|
|
65
|
+
TYPING_STOPPED = 'typing_stopped',
|
|
62
66
|
WARNING = 'warning',
|
|
63
67
|
WEBHOOK_CALLED = 'webhook_called'
|
|
64
68
|
}
|