@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/flow/FlowSearch.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { property, state, query } from 'lit/decorators.js';
|
|
1
|
+
import { property } from 'lit/decorators.js';
|
|
3
2
|
import {
|
|
4
3
|
FlowDefinition,
|
|
5
4
|
Node,
|
|
@@ -37,8 +36,8 @@ import {
|
|
|
37
36
|
NodeConfig
|
|
38
37
|
} from './types';
|
|
39
38
|
import { localizeAction } from './utils';
|
|
40
|
-
import { Icon } from '../Icons';
|
|
41
39
|
import { getTranslatableCategoriesForNode } from './categoryLocalization';
|
|
40
|
+
import { ModalSearchResult, SearchModal } from '../layout/SearchModal';
|
|
42
41
|
|
|
43
42
|
// Sticky note badge colors: border matches the sticky's border, fill is 25% lighter
|
|
44
43
|
const STICKY_BADGE_COLORS: Record<
|
|
@@ -52,24 +51,10 @@ const STICKY_BADGE_COLORS: Record<
|
|
|
52
51
|
gray: { border: '#6b7280', fill: '#8f949f', text: '#fff' }
|
|
53
52
|
};
|
|
54
53
|
|
|
55
|
-
export interface SearchResult {
|
|
54
|
+
export interface SearchResult extends ModalSearchResult {
|
|
56
55
|
nodeUuid: string;
|
|
57
56
|
// For actions: the action object; for nodes (splits): null
|
|
58
57
|
action: Action | null;
|
|
59
|
-
// The config name (e.g. "Send Message", "Call Webhook")
|
|
60
|
-
typeName: string;
|
|
61
|
-
// Color for the type badge background
|
|
62
|
-
color: string;
|
|
63
|
-
// Optional border color for the badge (used by sticky notes)
|
|
64
|
-
borderColor?: string;
|
|
65
|
-
// Optional text color override for the badge
|
|
66
|
-
textColor?: string;
|
|
67
|
-
// The full text that was searched
|
|
68
|
-
fullText: string;
|
|
69
|
-
// The index of the match start in fullText
|
|
70
|
-
matchStart: number;
|
|
71
|
-
// The length of the match
|
|
72
|
-
matchLength: number;
|
|
73
58
|
// For sticky notes: which field matched ('title' or 'body')
|
|
74
59
|
stickyField?: 'title' | 'body';
|
|
75
60
|
}
|
|
@@ -353,183 +338,7 @@ function localizeCategoryName(
|
|
|
353
338
|
return originalName;
|
|
354
339
|
}
|
|
355
340
|
|
|
356
|
-
export class FlowSearch extends
|
|
357
|
-
static styles = css`
|
|
358
|
-
:host {
|
|
359
|
-
position: fixed;
|
|
360
|
-
top: 0;
|
|
361
|
-
left: 0;
|
|
362
|
-
right: 0;
|
|
363
|
-
bottom: 0;
|
|
364
|
-
z-index: 100000;
|
|
365
|
-
display: none;
|
|
366
|
-
font-family: var(--font-family, sans-serif);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
:host([open]) {
|
|
370
|
-
display: flex;
|
|
371
|
-
align-items: flex-start;
|
|
372
|
-
justify-content: center;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
.backdrop {
|
|
376
|
-
position: absolute;
|
|
377
|
-
top: 0;
|
|
378
|
-
left: 0;
|
|
379
|
-
right: 0;
|
|
380
|
-
bottom: 0;
|
|
381
|
-
background: rgba(0, 0, 0, 0.3);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
.search-container {
|
|
385
|
-
position: relative;
|
|
386
|
-
margin-top: 80px;
|
|
387
|
-
width: 560px;
|
|
388
|
-
max-height: calc(100vh - 160px);
|
|
389
|
-
background: white;
|
|
390
|
-
border-radius: 14px;
|
|
391
|
-
box-shadow:
|
|
392
|
-
0 24px 80px rgba(0, 0, 0, 0.2),
|
|
393
|
-
0 0 0 1px rgba(0, 0, 0, 0.05);
|
|
394
|
-
display: flex;
|
|
395
|
-
flex-direction: column;
|
|
396
|
-
overflow: hidden;
|
|
397
|
-
animation: searchSlideIn 0.15s ease-out;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
@keyframes searchSlideIn {
|
|
401
|
-
from {
|
|
402
|
-
opacity: 0;
|
|
403
|
-
transform: scale(0.98) translateY(-8px);
|
|
404
|
-
}
|
|
405
|
-
to {
|
|
406
|
-
opacity: 1;
|
|
407
|
-
transform: scale(1) translateY(0);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
.search-input-row {
|
|
412
|
-
display: flex;
|
|
413
|
-
align-items: center;
|
|
414
|
-
padding: 14px 18px;
|
|
415
|
-
border-bottom: 1px solid #e5e7eb;
|
|
416
|
-
gap: 10px;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
.search-icon {
|
|
420
|
-
color: #9ca3af;
|
|
421
|
-
flex-shrink: 0;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
.search-icon temba-icon {
|
|
425
|
-
--icon-color: #9ca3af;
|
|
426
|
-
display: block;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
input {
|
|
430
|
-
flex: 1;
|
|
431
|
-
border: none;
|
|
432
|
-
outline: none;
|
|
433
|
-
font-size: 16px;
|
|
434
|
-
background: transparent;
|
|
435
|
-
color: #111;
|
|
436
|
-
font-family: inherit;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
input::placeholder {
|
|
440
|
-
color: #9ca3af;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
.results {
|
|
444
|
-
overflow-y: auto;
|
|
445
|
-
max-height: 400px;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
.result-item {
|
|
449
|
-
display: flex;
|
|
450
|
-
align-items: center;
|
|
451
|
-
padding: 10px 18px;
|
|
452
|
-
gap: 12px;
|
|
453
|
-
cursor: pointer;
|
|
454
|
-
border-bottom: 1px solid #f3f4f6;
|
|
455
|
-
transition: background 0.08s;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
.result-item:last-child {
|
|
459
|
-
border-bottom: none;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
.result-item:hover,
|
|
463
|
-
.result-item.highlighted {
|
|
464
|
-
background: #f0f4ff;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
.result-type-badge {
|
|
468
|
-
flex-shrink: 0;
|
|
469
|
-
font-size: 11px;
|
|
470
|
-
font-weight: 600;
|
|
471
|
-
color: white;
|
|
472
|
-
padding: 2px 10px;
|
|
473
|
-
border-radius: 8px;
|
|
474
|
-
border: 2px solid transparent;
|
|
475
|
-
white-space: nowrap;
|
|
476
|
-
text-align: center;
|
|
477
|
-
box-sizing: border-box;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
.result-text {
|
|
481
|
-
min-width: 0;
|
|
482
|
-
font-size: 14px;
|
|
483
|
-
color: #374151;
|
|
484
|
-
line-height: 1.4;
|
|
485
|
-
white-space: nowrap;
|
|
486
|
-
overflow: hidden;
|
|
487
|
-
text-overflow: ellipsis;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
.result-text mark {
|
|
491
|
-
background: #fef08a;
|
|
492
|
-
color: inherit;
|
|
493
|
-
border-radius: 2px;
|
|
494
|
-
padding: 0 1px;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
.no-results {
|
|
498
|
-
padding: 24px 18px;
|
|
499
|
-
text-align: center;
|
|
500
|
-
color: #9ca3af;
|
|
501
|
-
font-size: 14px;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
.result-count {
|
|
505
|
-
padding: 8px 18px;
|
|
506
|
-
font-size: 12px;
|
|
507
|
-
color: #9ca3af;
|
|
508
|
-
border-top: 1px solid #e5e7eb;
|
|
509
|
-
text-align: right;
|
|
510
|
-
flex-shrink: 0;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
.hint {
|
|
514
|
-
padding: 10px 18px;
|
|
515
|
-
font-size: 12px;
|
|
516
|
-
color: #9ca3af;
|
|
517
|
-
text-align: center;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
kbd {
|
|
521
|
-
background: #f3f4f6;
|
|
522
|
-
border: 1px solid #d1d5db;
|
|
523
|
-
border-radius: 4px;
|
|
524
|
-
padding: 1px 5px;
|
|
525
|
-
font-size: 11px;
|
|
526
|
-
font-family: inherit;
|
|
527
|
-
}
|
|
528
|
-
`;
|
|
529
|
-
|
|
530
|
-
@property({ type: Boolean, reflect: true })
|
|
531
|
-
open = false;
|
|
532
|
-
|
|
341
|
+
export class FlowSearch extends SearchModal<SearchResult> {
|
|
533
342
|
@property({ attribute: false })
|
|
534
343
|
definition: FlowDefinition | null = null;
|
|
535
344
|
|
|
@@ -542,96 +351,16 @@ export class FlowSearch extends LitElement {
|
|
|
542
351
|
@property({ type: Boolean })
|
|
543
352
|
includeCategories = false;
|
|
544
353
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
@state()
|
|
549
|
-
private results: SearchResult[] = [];
|
|
550
|
-
|
|
551
|
-
@state()
|
|
552
|
-
private highlightedIndex = 0;
|
|
553
|
-
|
|
554
|
-
@query('input')
|
|
555
|
-
private inputEl!: HTMLInputElement;
|
|
556
|
-
|
|
557
|
-
public show(): void {
|
|
558
|
-
this.open = true;
|
|
559
|
-
this.searchQuery = '';
|
|
560
|
-
this.results = [];
|
|
561
|
-
this.highlightedIndex = 0;
|
|
562
|
-
this.updateComplete.then(() => {
|
|
563
|
-
this.inputEl?.focus();
|
|
564
|
-
this.inputEl?.select();
|
|
565
|
-
});
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
public hide(): void {
|
|
569
|
-
this.open = false;
|
|
570
|
-
this.searchQuery = '';
|
|
571
|
-
this.results = [];
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
private handleInput(e: InputEvent): void {
|
|
575
|
-
const input = e.target as HTMLInputElement;
|
|
576
|
-
this.searchQuery = input.value;
|
|
577
|
-
this.performSearch();
|
|
578
|
-
this.highlightedIndex = 0;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
private handleKeyDown(e: KeyboardEvent): void {
|
|
582
|
-
if (e.key === 'Escape') {
|
|
583
|
-
e.preventDefault();
|
|
584
|
-
this.hide();
|
|
585
|
-
return;
|
|
586
|
-
}
|
|
587
|
-
if (e.key === 'ArrowDown' || (e.ctrlKey && e.key === 'n')) {
|
|
588
|
-
e.preventDefault();
|
|
589
|
-
if (this.results.length > 0) {
|
|
590
|
-
this.highlightedIndex =
|
|
591
|
-
(this.highlightedIndex + 1) % this.results.length;
|
|
592
|
-
}
|
|
593
|
-
return;
|
|
594
|
-
}
|
|
595
|
-
if (e.key === 'ArrowUp' || (e.ctrlKey && e.key === 'p')) {
|
|
596
|
-
e.preventDefault();
|
|
597
|
-
if (this.results.length > 0) {
|
|
598
|
-
this.highlightedIndex =
|
|
599
|
-
(this.highlightedIndex - 1 + this.results.length) %
|
|
600
|
-
this.results.length;
|
|
601
|
-
}
|
|
602
|
-
return;
|
|
603
|
-
}
|
|
604
|
-
if (e.key === 'Enter') {
|
|
605
|
-
e.preventDefault();
|
|
606
|
-
if (this.results.length > 0) {
|
|
607
|
-
this.selectResult(this.results[this.highlightedIndex]);
|
|
608
|
-
}
|
|
609
|
-
return;
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
private handleBackdropClick(): void {
|
|
614
|
-
this.hide();
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
private selectResult(result: SearchResult): void {
|
|
618
|
-
this.hide();
|
|
619
|
-
this.dispatchEvent(
|
|
620
|
-
new CustomEvent('temba-search-result-selected', {
|
|
621
|
-
detail: result,
|
|
622
|
-
bubbles: true,
|
|
623
|
-
composed: true
|
|
624
|
-
})
|
|
625
|
-
);
|
|
354
|
+
protected getSearchLabel(): string {
|
|
355
|
+
return this.scope === 'table' ? 'Search this table' : 'Search this flow';
|
|
626
356
|
}
|
|
627
357
|
|
|
628
|
-
|
|
629
|
-
if (!this.definition
|
|
630
|
-
|
|
631
|
-
return;
|
|
358
|
+
protected performSearch(rawQuery: string): SearchResult[] {
|
|
359
|
+
if (!this.definition) {
|
|
360
|
+
return [];
|
|
632
361
|
}
|
|
633
362
|
|
|
634
|
-
const query =
|
|
363
|
+
const query = rawQuery.toLowerCase();
|
|
635
364
|
|
|
636
365
|
// Get the localization map for the active language (if translating)
|
|
637
366
|
const isTranslating =
|
|
@@ -641,11 +370,10 @@ export class FlowSearch extends LitElement {
|
|
|
641
370
|
: undefined;
|
|
642
371
|
|
|
643
372
|
if (this.scope === 'table') {
|
|
644
|
-
|
|
645
|
-
return;
|
|
373
|
+
return this.performTableSearch(query, langLocalization);
|
|
646
374
|
}
|
|
647
375
|
|
|
648
|
-
|
|
376
|
+
return this.performFlowSearch(query, langLocalization);
|
|
649
377
|
}
|
|
650
378
|
|
|
651
379
|
private performFlowSearch(
|
|
@@ -889,112 +617,4 @@ export class FlowSearch extends LitElement {
|
|
|
889
617
|
|
|
890
618
|
return results;
|
|
891
619
|
}
|
|
892
|
-
|
|
893
|
-
private renderMatchText(result: SearchResult): TemplateResult {
|
|
894
|
-
const { matchStart, matchLength } = result;
|
|
895
|
-
const matchEnd = matchStart + matchLength;
|
|
896
|
-
|
|
897
|
-
// Replace newlines with spaces for single-line display
|
|
898
|
-
const text = result.fullText.replace(/\n/g, ' ');
|
|
899
|
-
|
|
900
|
-
// Type-name matches show an unhighlighted content preview
|
|
901
|
-
if (matchLength === 0) {
|
|
902
|
-
return html`${text}`;
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
// Show leading context before the match, then the match, then trailing.
|
|
906
|
-
// CSS text-overflow:ellipsis clips the trailing text, so we keep the match
|
|
907
|
-
// near the start. When the match is near the end of the text, show more
|
|
908
|
-
// leading context since there's less trailing text to display.
|
|
909
|
-
const afterMatch = text.length - matchEnd;
|
|
910
|
-
const contextBefore = afterMatch < 30 ? 50 : 20;
|
|
911
|
-
const start = Math.max(0, matchStart - contextBefore);
|
|
912
|
-
const prefix = start > 0 ? '\u2026' : '';
|
|
913
|
-
|
|
914
|
-
const before = text.slice(start, matchStart);
|
|
915
|
-
const match = text.slice(matchStart, matchEnd);
|
|
916
|
-
const after = text.slice(matchEnd);
|
|
917
|
-
|
|
918
|
-
return html`${prefix}${before}<mark>${match}</mark>${after}`;
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
updated(changedProps: Map<string, unknown>): void {
|
|
922
|
-
if (changedProps.has('highlightedIndex')) {
|
|
923
|
-
const highlighted = this.shadowRoot?.querySelector(
|
|
924
|
-
'.result-item.highlighted'
|
|
925
|
-
);
|
|
926
|
-
highlighted?.scrollIntoView({ block: 'nearest' });
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
render(): TemplateResult {
|
|
931
|
-
const isTableScope = this.scope === 'table';
|
|
932
|
-
const searchLabel = isTableScope ? 'Search this table' : 'Search this flow';
|
|
933
|
-
|
|
934
|
-
return html`
|
|
935
|
-
<div class="backdrop" @click=${this.handleBackdropClick}></div>
|
|
936
|
-
<div
|
|
937
|
-
class="search-container"
|
|
938
|
-
role="dialog"
|
|
939
|
-
aria-modal="true"
|
|
940
|
-
aria-label=${searchLabel}
|
|
941
|
-
@click=${(e: Event) => e.stopPropagation()}
|
|
942
|
-
>
|
|
943
|
-
<div class="search-input-row">
|
|
944
|
-
<div class="search-icon">
|
|
945
|
-
<temba-icon name=${Icon.search} size="1.5"></temba-icon>
|
|
946
|
-
</div>
|
|
947
|
-
<input
|
|
948
|
-
type="text"
|
|
949
|
-
placeholder="${searchLabel}..."
|
|
950
|
-
aria-label=${searchLabel}
|
|
951
|
-
.value=${this.searchQuery}
|
|
952
|
-
@input=${this.handleInput}
|
|
953
|
-
@keydown=${this.handleKeyDown}
|
|
954
|
-
/>
|
|
955
|
-
</div>
|
|
956
|
-
${this.searchQuery.trim()
|
|
957
|
-
? this.results.length > 0
|
|
958
|
-
? html`
|
|
959
|
-
<div class="results">
|
|
960
|
-
${this.results.map(
|
|
961
|
-
(result, index) => html`
|
|
962
|
-
<div
|
|
963
|
-
class="result-item ${index === this.highlightedIndex
|
|
964
|
-
? 'highlighted'
|
|
965
|
-
: ''}"
|
|
966
|
-
@click=${() => this.selectResult(result)}
|
|
967
|
-
@mouseenter=${() => {
|
|
968
|
-
this.highlightedIndex = index;
|
|
969
|
-
}}
|
|
970
|
-
>
|
|
971
|
-
<div
|
|
972
|
-
class="result-type-badge"
|
|
973
|
-
style="background:${result.color};border-color:${result.borderColor ||
|
|
974
|
-
result.color}${result.textColor
|
|
975
|
-
? `;color:${result.textColor}`
|
|
976
|
-
: ''}"
|
|
977
|
-
>
|
|
978
|
-
${result.typeName}
|
|
979
|
-
</div>
|
|
980
|
-
<div class="result-text">
|
|
981
|
-
${this.renderMatchText(result)}
|
|
982
|
-
</div>
|
|
983
|
-
</div>
|
|
984
|
-
`
|
|
985
|
-
)}
|
|
986
|
-
</div>
|
|
987
|
-
<div class="result-count">
|
|
988
|
-
${this.results.length}
|
|
989
|
-
result${this.results.length !== 1 ? 's' : ''}
|
|
990
|
-
</div>
|
|
991
|
-
`
|
|
992
|
-
: html`<div class="no-results">No matches found</div>`
|
|
993
|
-
: html`<div class="hint">
|
|
994
|
-
<kbd>↑</kbd> <kbd>↓</kbd> to navigate <kbd>Enter</kbd> to
|
|
995
|
-
open <kbd>Esc</kbd> to close
|
|
996
|
-
</div>`}
|
|
997
|
-
</div>
|
|
998
|
-
`;
|
|
999
|
-
}
|
|
1000
620
|
}
|
package/src/form/RangePicker.ts
CHANGED
|
@@ -123,12 +123,20 @@ export class RangePicker extends RapidElement {
|
|
|
123
123
|
/* The hover wash sits on the sunken track, so --sunken itself
|
|
124
124
|
would be invisible here. Derived from --text-1 rather than a raw
|
|
125
125
|
rgba literal so it follows the palette (the tokens file mixes
|
|
126
|
-
against transparent the same way for --focus-halo).
|
|
126
|
+
against transparent the same way for --focus-halo). The rgba
|
|
127
|
+
fallback pre-mixes the default --text-1 for browsers without
|
|
128
|
+
color-mix(). */
|
|
127
129
|
.range-btn:hover:not(.selected) {
|
|
128
|
-
background:
|
|
130
|
+
background: rgba(26, 31, 38, 0.06);
|
|
129
131
|
color: var(--text-1);
|
|
130
132
|
}
|
|
131
133
|
|
|
134
|
+
@supports (color: color-mix(in srgb, red, red)) {
|
|
135
|
+
.range-btn:hover:not(.selected) {
|
|
136
|
+
background: color-mix(in srgb, var(--text-1) 6%, transparent);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
132
140
|
.range-btn.selected {
|
|
133
141
|
background: var(--surface);
|
|
134
142
|
color: var(--accent-700);
|
|
@@ -371,14 +371,27 @@ export class Select<T extends SelectOption> extends FieldElement {
|
|
|
371
371
|
margin: 0;
|
|
372
372
|
border: 0;
|
|
373
373
|
border-radius: 999px;
|
|
374
|
-
|
|
374
|
+
/* Neutral wash first for browsers without color-mix(); the
|
|
375
|
+
currentColor-tinted version below needs the @supports gate
|
|
376
|
+
because currentColor keeps the declaration valid until
|
|
377
|
+
computed-value time. */
|
|
378
|
+
background: rgba(0, 0, 0, 0.12);
|
|
375
379
|
color: inherit;
|
|
376
380
|
opacity: 0.8;
|
|
377
381
|
--icon-color: currentColor;
|
|
378
382
|
}
|
|
379
383
|
.multi .remove-item:hover {
|
|
380
384
|
opacity: 1;
|
|
381
|
-
background:
|
|
385
|
+
background: rgba(0, 0, 0, 0.22);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
@supports (color: color-mix(in srgb, red, red)) {
|
|
389
|
+
.multi .remove-item {
|
|
390
|
+
background: color-mix(in oklab, currentColor 25%, transparent);
|
|
391
|
+
}
|
|
392
|
+
.multi .remove-item:hover {
|
|
393
|
+
background: color-mix(in oklab, currentColor 45%, transparent);
|
|
394
|
+
}
|
|
382
395
|
}
|
|
383
396
|
|
|
384
397
|
input {
|