@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/src/store/Store.ts
CHANGED
|
@@ -25,8 +25,10 @@ import { RapidElement } from '../RapidElement';
|
|
|
25
25
|
import {
|
|
26
26
|
RealtimeSubscription,
|
|
27
27
|
setRealtimeContext,
|
|
28
|
-
subscribeToOrganization
|
|
28
|
+
subscribeToOrganization,
|
|
29
|
+
OrganizationEvent
|
|
29
30
|
} from '../live/Realtime';
|
|
31
|
+
import { Watchers } from '../live/Watchers';
|
|
30
32
|
import { lru } from 'tiny-lru';
|
|
31
33
|
import { DateTime } from 'luxon';
|
|
32
34
|
import { css, html } from 'lit';
|
|
@@ -229,7 +231,7 @@ export class Store extends RapidElement {
|
|
|
229
231
|
private shortcuts: Shortcut[] = [];
|
|
230
232
|
private workspace: Workspace;
|
|
231
233
|
private featuredFields: ContactField[] = [];
|
|
232
|
-
private assetWatchers
|
|
234
|
+
private assetWatchers = new Watchers<AssetWatcher>('store asset watcher');
|
|
233
235
|
// canonical names, the identities we've already asked about (including ones
|
|
234
236
|
// the endpoint had no asset for) and their last-write versions, all bounded
|
|
235
237
|
// so a long-lived page doesn't keep every asset it has ever rendered
|
|
@@ -729,19 +731,12 @@ export class Store extends RapidElement {
|
|
|
729
731
|
})),
|
|
730
732
|
onEvent
|
|
731
733
|
};
|
|
732
|
-
this.assetWatchers.
|
|
733
|
-
|
|
734
|
-
if (this.assetWatchers.includes(watcher)) {
|
|
735
|
-
this.deliverAssetEvent(watcher, null);
|
|
736
|
-
}
|
|
737
|
-
});
|
|
734
|
+
this.assetWatchers.add(watcher);
|
|
735
|
+
this.assetWatchers.prime(watcher, () => watcher.onEvent(null));
|
|
738
736
|
|
|
739
737
|
return {
|
|
740
738
|
unsubscribe: () => {
|
|
741
|
-
|
|
742
|
-
if (index >= 0) {
|
|
743
|
-
this.assetWatchers.splice(index, 1);
|
|
744
|
-
}
|
|
739
|
+
this.assetWatchers.remove(watcher);
|
|
745
740
|
}
|
|
746
741
|
};
|
|
747
742
|
}
|
|
@@ -754,7 +749,7 @@ export class Store extends RapidElement {
|
|
|
754
749
|
*/
|
|
755
750
|
private async refreshAssetCache(): Promise<void> {
|
|
756
751
|
const interests = new Map<string, StoreAssetReference>();
|
|
757
|
-
for (const watcher of this.assetWatchers) {
|
|
752
|
+
for (const watcher of this.assetWatchers.all()) {
|
|
758
753
|
for (const interest of watcher.interests) {
|
|
759
754
|
const identity = assetIdentity(interest);
|
|
760
755
|
if (identity) {
|
|
@@ -765,9 +760,7 @@ export class Store extends RapidElement {
|
|
|
765
760
|
if (interests.size > 0) {
|
|
766
761
|
await this.resolveAssets([...interests.values()], true);
|
|
767
762
|
}
|
|
768
|
-
|
|
769
|
-
this.deliverAssetEvent(watcher, null);
|
|
770
|
-
}
|
|
763
|
+
this.assetWatchers.each((watcher) => watcher.onEvent(null));
|
|
771
764
|
}
|
|
772
765
|
|
|
773
766
|
private async fetchAssetBatch(
|
|
@@ -863,8 +856,8 @@ export class Store extends RapidElement {
|
|
|
863
856
|
this.assetVersions.set(identity, ++this.assetVersion);
|
|
864
857
|
}
|
|
865
858
|
|
|
866
|
-
private handleOrganizationEvent(event:
|
|
867
|
-
const asset = event?.asset
|
|
859
|
+
private handleOrganizationEvent(event: OrganizationEvent): void {
|
|
860
|
+
const asset = event?.asset;
|
|
868
861
|
const identity = assetIdentity(asset);
|
|
869
862
|
if (
|
|
870
863
|
event?.type !== 'asset_changed' ||
|
|
@@ -875,7 +868,7 @@ export class Store extends RapidElement {
|
|
|
875
868
|
}
|
|
876
869
|
|
|
877
870
|
const changed = { ...asset };
|
|
878
|
-
const interested = this.assetWatchers.
|
|
871
|
+
const interested = this.assetWatchers.some((watcher) =>
|
|
879
872
|
watchesAsset(watcher, changed)
|
|
880
873
|
);
|
|
881
874
|
const previouslyResolved = this.resolvedAssetIdentities.has(identity);
|
|
@@ -892,7 +885,7 @@ export class Store extends RapidElement {
|
|
|
892
885
|
name: changed.name
|
|
893
886
|
};
|
|
894
887
|
}
|
|
895
|
-
if (!previouslyResolved && !pending && interested
|
|
888
|
+
if (!previouslyResolved && !pending && !interested) {
|
|
896
889
|
return;
|
|
897
890
|
}
|
|
898
891
|
|
|
@@ -904,20 +897,10 @@ export class Store extends RapidElement {
|
|
|
904
897
|
type: 'asset_changed',
|
|
905
898
|
asset: changed
|
|
906
899
|
};
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
private deliverAssetEvent(
|
|
913
|
-
watcher: AssetWatcher,
|
|
914
|
-
event: StoreAssetChangedEvent | null
|
|
915
|
-
): void {
|
|
916
|
-
try {
|
|
917
|
-
watcher.onEvent(event);
|
|
918
|
-
} catch (error) {
|
|
919
|
-
console.error('store asset watcher failed', error);
|
|
920
|
-
}
|
|
900
|
+
this.assetWatchers.each(
|
|
901
|
+
(watcher) => watcher.onEvent(publication),
|
|
902
|
+
(watcher) => watchesAsset(watcher, changed)
|
|
903
|
+
);
|
|
921
904
|
}
|
|
922
905
|
|
|
923
906
|
public isDynamicGroup(uuid: string): boolean {
|
|
@@ -20,16 +20,21 @@ export const designTokens = css`
|
|
|
20
20
|
Fallback matches the design-system.css default so component-
|
|
21
21
|
only test harnesses pick up the same brand colour. */
|
|
22
22
|
--accent: rgb(var(--primary-rgb, 73, 127, 206));
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
/* Static ramp values are pre-mixed from the default anchor for
|
|
24
|
+
browsers without color-mix() (pre-Chrome 111, e.g. the last
|
|
25
|
+
Chrome available on Windows 7/8.1). The @supports block below
|
|
26
|
+
re-derives them from --accent so host re-theming still works
|
|
27
|
+
where color-mix() is available. */
|
|
28
|
+
--accent-50: #f4f7fc;
|
|
29
|
+
--accent-100: #e2ebf7;
|
|
30
|
+
--accent-200: #c5d6ef;
|
|
31
|
+
--accent-300: #92b2e2;
|
|
27
32
|
--accent-400: var(--accent);
|
|
28
|
-
--accent-500:
|
|
29
|
-
--accent-600:
|
|
30
|
-
--accent-700:
|
|
31
|
-
--accent-800:
|
|
32
|
-
--accent-900:
|
|
33
|
+
--accent-500: #4272b9;
|
|
34
|
+
--accent-600: #3a66a5;
|
|
35
|
+
--accent-700: #2f5386;
|
|
36
|
+
--accent-800: #244067;
|
|
37
|
+
--accent-900: #1a2c48;
|
|
33
38
|
|
|
34
39
|
/* neutrals */
|
|
35
40
|
--bg: #f6f7f9;
|
|
@@ -124,14 +129,19 @@ export const designTokens = css`
|
|
|
124
129
|
error state (e.g. the dropdown popup) reference --focus-muted
|
|
125
130
|
/ --focus-halo directly to skip that override. */
|
|
126
131
|
--focus: rgb(var(--focus-rgb, 91, 156, 229));
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
--focus-
|
|
130
|
-
--focus-
|
|
131
|
-
--focus-
|
|
132
|
-
--focus-
|
|
133
|
-
--focus-
|
|
134
|
-
--focus-
|
|
132
|
+
/* Static values pre-mixed from the default focus hue for browsers
|
|
133
|
+
without color-mix(); re-derived in the @supports block below. */
|
|
134
|
+
--focus-50: #ebf3fc;
|
|
135
|
+
--focus-100: #d8e7f9;
|
|
136
|
+
--focus-200: #bdd7f5;
|
|
137
|
+
--focus-300: #9dc4ef;
|
|
138
|
+
--focus-600: #375e89;
|
|
139
|
+
--focus-700: #294667;
|
|
140
|
+
--focus-muted: #9dc4ef;
|
|
141
|
+
/* The halo is a translucent wash, so unlike the opaque ramp steps
|
|
142
|
+
it can keep tracking the theme without color-mix() — rgba(var())
|
|
143
|
+
substitution works wherever custom properties do. */
|
|
144
|
+
--focus-halo: 0 0 0 3px rgba(var(--focus-rgb, 91, 156, 229), 0.3);
|
|
135
145
|
--color-focus: var(--focus-muted);
|
|
136
146
|
--widget-box-shadow-focused: var(--focus-halo);
|
|
137
147
|
|
|
@@ -160,4 +170,34 @@ export const designTokens = css`
|
|
|
160
170
|
--temba-select-selected-font-size: 13.5px;
|
|
161
171
|
--temba-select-min-height: var(--input-h);
|
|
162
172
|
}
|
|
173
|
+
|
|
174
|
+
/* Where color-mix() is supported, derive the accent / focus ramps
|
|
175
|
+
from their anchors so host pages can re-theme by overriding just
|
|
176
|
+
--primary-rgb / --focus-rgb. Browsers without color-mix() keep the
|
|
177
|
+
static pre-mixed values above. Note the static values can't act as
|
|
178
|
+
per-declaration fallbacks — a custom property is never invalid at
|
|
179
|
+
parse time, so the last declaration always wins — hence the
|
|
180
|
+
@supports gate. */
|
|
181
|
+
@supports (color: color-mix(in srgb, red, red)) {
|
|
182
|
+
:host {
|
|
183
|
+
--accent-50: color-mix(in srgb, var(--accent) 6%, white);
|
|
184
|
+
--accent-100: color-mix(in srgb, var(--accent) 16%, white);
|
|
185
|
+
--accent-200: color-mix(in srgb, var(--accent) 32%, white);
|
|
186
|
+
--accent-300: color-mix(in srgb, var(--accent) 60%, white);
|
|
187
|
+
--accent-500: color-mix(in srgb, var(--accent) 90%, black);
|
|
188
|
+
--accent-600: color-mix(in srgb, var(--accent) 80%, black);
|
|
189
|
+
--accent-700: color-mix(in srgb, var(--accent) 65%, black);
|
|
190
|
+
--accent-800: color-mix(in srgb, var(--accent) 50%, black);
|
|
191
|
+
--accent-900: color-mix(in srgb, var(--accent) 35%, black);
|
|
192
|
+
|
|
193
|
+
--focus-50: color-mix(in srgb, var(--focus) 12%, white);
|
|
194
|
+
--focus-100: color-mix(in srgb, var(--focus) 24%, white);
|
|
195
|
+
--focus-200: color-mix(in srgb, var(--focus) 40%, white);
|
|
196
|
+
--focus-300: color-mix(in srgb, var(--focus) 60%, white);
|
|
197
|
+
--focus-600: color-mix(in srgb, var(--focus) 60%, black);
|
|
198
|
+
--focus-700: color-mix(in srgb, var(--focus) 45%, black);
|
|
199
|
+
--focus-muted: color-mix(in srgb, var(--focus) 60%, white);
|
|
200
|
+
--focus-halo: 0 0 0 3px color-mix(in srgb, var(--focus) 30%, transparent);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
163
203
|
`;
|
|
@@ -100,10 +100,14 @@ export const pillVariants = css`
|
|
|
100
100
|
border-color: var(--pill-border, var(--border));
|
|
101
101
|
--icon-color: var(--text-2);
|
|
102
102
|
}
|
|
103
|
+
/* Anchor-derived variants carry static pre-mixed bg/border values
|
|
104
|
+
for browsers without color-mix() (pre-Chrome 111); the @supports
|
|
105
|
+
block at the bottom re-derives them from the anchors so host
|
|
106
|
+
re-theming still works where color-mix() is available. */
|
|
103
107
|
.pill-flow {
|
|
104
|
-
background:
|
|
108
|
+
background: #e3f4e9;
|
|
105
109
|
color: var(--flow);
|
|
106
|
-
border-color:
|
|
110
|
+
border-color: #c5e8d2;
|
|
107
111
|
--icon-color: var(--flow);
|
|
108
112
|
}
|
|
109
113
|
/* Recipient color — shared by contacts and groups. Anchored to
|
|
@@ -111,27 +115,27 @@ export const pillVariants = css`
|
|
|
111
115
|
identity even when the host page re-themes via --primary-rgb. */
|
|
112
116
|
.pill-contact,
|
|
113
117
|
.pill-group {
|
|
114
|
-
background:
|
|
118
|
+
background: #e5eef6;
|
|
115
119
|
color: var(--recipient);
|
|
116
|
-
border-color:
|
|
120
|
+
border-color: #cadbec;
|
|
117
121
|
--icon-color: var(--recipient);
|
|
118
122
|
}
|
|
119
123
|
.pill-channel {
|
|
120
|
-
background:
|
|
124
|
+
background: #ede4f5;
|
|
121
125
|
color: var(--channel);
|
|
122
|
-
border-color:
|
|
126
|
+
border-color: #dac8e9;
|
|
123
127
|
--icon-color: var(--channel);
|
|
124
128
|
}
|
|
125
129
|
.pill-topic {
|
|
126
|
-
background:
|
|
130
|
+
background: #faefe1;
|
|
127
131
|
color: var(--topic);
|
|
128
|
-
border-color:
|
|
132
|
+
border-color: #f6ddc1;
|
|
129
133
|
--icon-color: var(--topic);
|
|
130
134
|
}
|
|
131
135
|
.pill-campaign {
|
|
132
|
-
background:
|
|
136
|
+
background: #e1f2f6;
|
|
133
137
|
color: var(--campaign);
|
|
134
|
-
border-color:
|
|
138
|
+
border-color: #c1e4ec;
|
|
135
139
|
--icon-color: var(--campaign);
|
|
136
140
|
}
|
|
137
141
|
.pill-field {
|
|
@@ -171,4 +175,32 @@ export const pillVariants = css`
|
|
|
171
175
|
border-color: var(--pill-border, var(--border));
|
|
172
176
|
--icon-color: var(--text-2);
|
|
173
177
|
}
|
|
178
|
+
|
|
179
|
+
/* Anchor-derived bg/border for browsers with color-mix(). These
|
|
180
|
+
can't be simple same-rule fallbacks: a declaration containing
|
|
181
|
+
var() isn't dropped at parse time, it goes invalid at
|
|
182
|
+
computed-value time and takes the earlier declaration with it. */
|
|
183
|
+
@supports (color: color-mix(in srgb, red, red)) {
|
|
184
|
+
.pill-flow {
|
|
185
|
+
background: color-mix(in srgb, var(--flow) 12%, white);
|
|
186
|
+
border-color: color-mix(in srgb, var(--flow) 25%, white);
|
|
187
|
+
}
|
|
188
|
+
.pill-contact,
|
|
189
|
+
.pill-group {
|
|
190
|
+
background: color-mix(in srgb, var(--recipient) 12%, white);
|
|
191
|
+
border-color: color-mix(in srgb, var(--recipient) 25%, white);
|
|
192
|
+
}
|
|
193
|
+
.pill-channel {
|
|
194
|
+
background: color-mix(in srgb, var(--channel) 12%, white);
|
|
195
|
+
border-color: color-mix(in srgb, var(--channel) 25%, white);
|
|
196
|
+
}
|
|
197
|
+
.pill-topic {
|
|
198
|
+
background: color-mix(in srgb, var(--topic) 12%, white);
|
|
199
|
+
border-color: color-mix(in srgb, var(--topic) 25%, white);
|
|
200
|
+
}
|
|
201
|
+
.pill-campaign {
|
|
202
|
+
background: color-mix(in srgb, var(--campaign) 12%, white);
|
|
203
|
+
border-color: color-mix(in srgb, var(--campaign) 25%, white);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
174
206
|
`;
|
|
@@ -20,18 +20,22 @@
|
|
|
20
20
|
--primary-rgb: 73, 127, 206;
|
|
21
21
|
|
|
22
22
|
/* accent ramp — derived from --primary-rgb via sRGB mixing.
|
|
23
|
-
400 == primary; the ramp computes outward in both directions.
|
|
23
|
+
400 == primary; the ramp computes outward in both directions.
|
|
24
|
+
Static values pre-mixed from the default anchor come first for
|
|
25
|
+
browsers without color-mix() (pre-Chrome 111); the @supports
|
|
26
|
+
block below re-derives them from --accent so re-theming via
|
|
27
|
+
--primary-rgb still works where color-mix() is available. */
|
|
24
28
|
--accent: rgb(var(--primary-rgb));
|
|
25
|
-
--accent-50:
|
|
26
|
-
--accent-100:
|
|
27
|
-
--accent-200:
|
|
28
|
-
--accent-300:
|
|
29
|
+
--accent-50: #f4f7fc;
|
|
30
|
+
--accent-100: #e2ebf7;
|
|
31
|
+
--accent-200: #c5d6ef;
|
|
32
|
+
--accent-300: #92b2e2;
|
|
29
33
|
--accent-400: var(--accent);
|
|
30
|
-
--accent-500:
|
|
31
|
-
--accent-600:
|
|
32
|
-
--accent-700:
|
|
33
|
-
--accent-800:
|
|
34
|
-
--accent-900:
|
|
34
|
+
--accent-500: #4272b9;
|
|
35
|
+
--accent-600: #3a66a5;
|
|
36
|
+
--accent-700: #2f5386;
|
|
37
|
+
--accent-800: #244067;
|
|
38
|
+
--accent-900: #1a2c48;
|
|
35
39
|
|
|
36
40
|
/* neutrals */
|
|
37
41
|
--bg: #F6F7F9;
|
|
@@ -94,6 +98,24 @@
|
|
|
94
98
|
--section-w-collapsed: 56px;
|
|
95
99
|
}
|
|
96
100
|
|
|
101
|
+
/* Where color-mix() is supported, derive the accent ramp from --accent
|
|
102
|
+
so re-theming via --primary-rgb works without design-system.js.
|
|
103
|
+
Custom properties can't fall back per-declaration (they're never
|
|
104
|
+
invalid at parse time), hence the @supports gate. */
|
|
105
|
+
@supports (color: color-mix(in srgb, red, red)) {
|
|
106
|
+
:root {
|
|
107
|
+
--accent-50: color-mix(in srgb, var(--accent) 6%, white);
|
|
108
|
+
--accent-100: color-mix(in srgb, var(--accent) 16%, white);
|
|
109
|
+
--accent-200: color-mix(in srgb, var(--accent) 32%, white);
|
|
110
|
+
--accent-300: color-mix(in srgb, var(--accent) 60%, white);
|
|
111
|
+
--accent-500: color-mix(in srgb, var(--accent) 90%, black);
|
|
112
|
+
--accent-600: color-mix(in srgb, var(--accent) 80%, black);
|
|
113
|
+
--accent-700: color-mix(in srgb, var(--accent) 65%, black);
|
|
114
|
+
--accent-800: color-mix(in srgb, var(--accent) 50%, black);
|
|
115
|
+
--accent-900: color-mix(in srgb, var(--accent) 35%, black);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
97
119
|
/* ─── base ───────────────────────────────────────────────────────────── */
|
|
98
120
|
.ds * { box-sizing: border-box; }
|
|
99
121
|
.ds {
|
|
@@ -364,7 +386,13 @@
|
|
|
364
386
|
color: white;
|
|
365
387
|
}
|
|
366
388
|
.ds .btn-danger:hover {
|
|
367
|
-
|
|
389
|
+
/* pre-mixed fallback for browsers without color-mix() */
|
|
390
|
+
background: #b73737;
|
|
391
|
+
}
|
|
392
|
+
@supports (color: color-mix(in srgb, red, red)) {
|
|
393
|
+
.ds .btn-danger:hover {
|
|
394
|
+
background: color-mix(in srgb, var(--danger) 88%, black);
|
|
395
|
+
}
|
|
368
396
|
}
|
|
369
397
|
.ds .btn.is-disabled,
|
|
370
398
|
.ds .btn:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
@@ -533,7 +561,14 @@
|
|
|
533
561
|
.ds .input:focus-within {
|
|
534
562
|
outline: 0;
|
|
535
563
|
border-color: var(--accent-300);
|
|
536
|
-
|
|
564
|
+
/* translucent-ring fallback for browsers without color-mix() */
|
|
565
|
+
box-shadow: 0 0 0 3px rgba(var(--primary-rgb), 0.25);
|
|
566
|
+
}
|
|
567
|
+
@supports (color: color-mix(in srgb, red, red)) {
|
|
568
|
+
.ds .input:focus,
|
|
569
|
+
.ds .input:focus-within {
|
|
570
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent);
|
|
571
|
+
}
|
|
537
572
|
}
|
|
538
573
|
|
|
539
574
|
.ds .input-tokens {
|
|
@@ -41,15 +41,19 @@
|
|
|
41
41
|
--curvature: 6px;
|
|
42
42
|
--curvature-widget: 6px;
|
|
43
43
|
--focus: rgb(var(--focus-rgb, 91, 156, 229));
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
--focus-
|
|
47
|
-
--focus-
|
|
48
|
-
--focus-
|
|
49
|
-
--focus-
|
|
50
|
-
--focus-
|
|
51
|
-
--focus-
|
|
52
|
-
|
|
44
|
+
/* Static values pre-mixed from the default focus hue for browsers
|
|
45
|
+
without color-mix(); re-derived in the @supports block below. */
|
|
46
|
+
--focus-50: #ebf3fc;
|
|
47
|
+
--focus-100: #d8e7f9;
|
|
48
|
+
--focus-200: #bdd7f5;
|
|
49
|
+
--focus-300: #9dc4ef;
|
|
50
|
+
--focus-600: #375e89;
|
|
51
|
+
--focus-700: #294667;
|
|
52
|
+
--focus-muted: #9dc4ef;
|
|
53
|
+
/* The halo is a translucent wash, so unlike the opaque ramp steps
|
|
54
|
+
it can keep tracking the theme without color-mix() — rgba(var())
|
|
55
|
+
substitution works wherever custom properties do. */
|
|
56
|
+
--focus-halo: 0 0 0 3px rgba(var(--focus-rgb, 91, 156, 229), 0.3);
|
|
53
57
|
--color-focus: #a4cafe;
|
|
54
58
|
--color-widget-bg: #fff;
|
|
55
59
|
--color-widget-bg-focused: #fff;
|
|
@@ -201,6 +205,24 @@
|
|
|
201
205
|
|
|
202
206
|
}
|
|
203
207
|
|
|
208
|
+
/* Where color-mix() is supported, derive the focus ramp from --focus
|
|
209
|
+
so re-theming via --focus-rgb works. Custom properties can't fall
|
|
210
|
+
back per-declaration (they're never invalid at parse time), hence
|
|
211
|
+
the @supports gate. */
|
|
212
|
+
@supports (color: color-mix(in srgb, red, red)) {
|
|
213
|
+
html {
|
|
214
|
+
--focus-50: color-mix(in srgb, var(--focus) 12%, white);
|
|
215
|
+
--focus-100: color-mix(in srgb, var(--focus) 24%, white);
|
|
216
|
+
--focus-200: color-mix(in srgb, var(--focus) 40%, white);
|
|
217
|
+
--focus-300: color-mix(in srgb, var(--focus) 60%, white);
|
|
218
|
+
--focus-600: color-mix(in srgb, var(--focus) 60%, black);
|
|
219
|
+
--focus-700: color-mix(in srgb, var(--focus) 45%, black);
|
|
220
|
+
--focus-muted: color-mix(in srgb, var(--focus) 60%, white);
|
|
221
|
+
--focus-halo: 0 0 0 3px
|
|
222
|
+
color-mix(in srgb, var(--focus) 30%, transparent);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
204
226
|
temba-button {
|
|
205
227
|
--button-bg: var(--accent-500);
|
|
206
228
|
--button-text: var(--color-text-light);
|
package/temba-modules.ts
CHANGED
|
@@ -95,6 +95,7 @@ import { CardStack } from './src/layout/CardStack';
|
|
|
95
95
|
import { CardLayout } from './src/layout/CardLayout';
|
|
96
96
|
import { Simulator } from './src/simulator/Simulator';
|
|
97
97
|
import { FlowSearch } from './src/flow/FlowSearch';
|
|
98
|
+
import { TicketSearch } from './src/live/TicketSearch';
|
|
98
99
|
import { IssuesWindow } from './src/flow/IssuesWindow';
|
|
99
100
|
import { RevisionsWindow } from './src/flow/RevisionsWindow';
|
|
100
101
|
import { AutoTranslate } from './src/flow/AutoTranslate';
|
|
@@ -205,6 +206,7 @@ addCustomElement('temba-floating-tab', FloatingTab);
|
|
|
205
206
|
addCustomElement('temba-floating-window', FloatingWindow);
|
|
206
207
|
addCustomElement('temba-simulator', Simulator);
|
|
207
208
|
addCustomElement('temba-flow-search', FlowSearch);
|
|
209
|
+
addCustomElement('temba-ticket-search', TicketSearch);
|
|
208
210
|
addCustomElement('temba-issues-window', IssuesWindow);
|
|
209
211
|
addCustomElement('temba-revisions-window', RevisionsWindow);
|
|
210
212
|
addCustomElement('temba-auto-translate', AutoTranslate);
|