mention-picker.svelte RawHistoryFolder
1<script lang="ts">
2 import type { SafeUser } from '../types/chat';
3
4 // Props
5 let {
6 suggestions,
7 selectedIndex,
8 activeId,
9 listBoxId,
10 listStyle,
11 isPortal = false,
12 emptyMessage = 'Aucun utilisateur',
13 onSelect,
14 onMouseDown
15 } = $props<{
16 suggestions: SafeUser[];
17 selectedIndex: number;
18 activeId: string | null;
19 listBoxId: string;
20 listStyle: string;
21 isPortal?: boolean;
22 emptyMessage?: string;
23 onSelect: (user: SafeUser) => void;
24 onMouseDown?: (event: MouseEvent) => void;
25 }>();
26
27 // Generate option ID from user ID
28 function getOptionId(userId: string): string {
29 return `${listBoxId}-${userId}`;
30 }
31
32 function handleOptionMouseDown(event: MouseEvent, user: SafeUser) {
33 event.preventDefault();
34 onSelect(user);
35 }
36
37 function handleContainerMouseDown(event: MouseEvent) {
38 event.preventDefault();
39 onMouseDown?.(event);
40 }
41</script>
42
43<div
44 id={listBoxId}
45 class="mention-picker"
46 class:mention-picker-portal={isPortal}
47 role="listbox"
48 aria-label="Suggestions de mentions"
49 aria-activedescendant={activeId}
50 tabindex="-1"
51 style={`z-index: ${isPortal ? 3000 : 10}; ${listStyle}`}
52 onmousedown={handleContainerMouseDown}
53>
54 {#if suggestions.length > 0}
55 {#each suggestions as user, index (user.id)}
56 <div
57 id={getOptionId(user.id)}
58 role="option"
59 class="mention-option"
60 class:selected={index === selectedIndex}
61 aria-selected={index === selectedIndex}
62 tabindex="-1"
63 onmousedown={(event) => handleOptionMouseDown(event, user)}
64 >
65 <span class="mention-name">{user.nickname}</span>
66 <span class="mention-status {user.status}">{user.status}</span>
67 </div>
68 {/each}
69 {:else}
70 <div
71 class="mention-empty"
72 role="option"
73 aria-disabled="true"
74 aria-selected="false"
75 tabindex="-1"
76 >
77 {emptyMessage}
78 </div>
79 {/if}
80</div>
81
82<style>
83 .mention-picker {
84 position: absolute;
85 padding: 0.35rem;
86 max-height: 220px;
87 overflow-y: auto;
88 background: #fffef5;
89 border: 2px inset #dfdfdf;
90 box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.15);
91 font-family: 'Pixelated MS Sans Serif', Tahoma, sans-serif;
92 font-size: 0.85rem;
93 min-width: 220px;
94 }
95
96 .mention-picker-portal {
97 position: fixed;
98 }
99
100 .mention-option {
101 display: flex;
102 align-items: center;
103 justify-content: space-between;
104 gap: 0.5rem;
105 padding: 0.25rem 0.4rem;
106 border: 1px solid transparent;
107 cursor: pointer;
108 }
109
110 .mention-option.selected {
111 background: #dbe8ff;
112 border-color: #2d31a6;
113 }
114
115 .mention-name {
116 font-weight: bold;
117 color: #1e2a72;
118 }
119
120 .mention-status {
121 font-size: 0.7rem;
122 text-transform: uppercase;
123 letter-spacing: 0.05em;
124 color: #5b5b5b;
125 }
126
127 .mention-status.online {
128 color: #1f7a1f;
129 }
130
131 .mention-status.away,
132 .mention-status.busy {
133 color: #b06a00;
134 }
135
136 .mention-status.offline {
137 color: #8a8a8a;
138 }
139
140 .mention-empty {
141 padding: 0.35rem 0.5rem;
142 color: #777;
143 font-style: italic;
144 }
145</style>