tangled
alpha
login
or
join now
zzstoatzz.io
/
status
0
fork
atom
slack status without the slack
status.zzstoatzz.io/
quickslice
0
fork
atom
overview
issues
pulls
pipelines
Redesign emoji picker as immersive modal
zzstoatzz.io
6 months ago
e245682e
cfd09b95
+365
-100
1 changed file
expand all
collapse all
unified
split
templates
status.html
+365
-100
templates/status.html
···
190
</form>
191
</div>
192
193
-
<!-- Emoji Picker (hidden by default) -->
194
-
<div class="emoji-picker" id="emoji-picker" style="display: none;">
195
-
<div class="emoji-search-container">
196
-
<input type="text"
197
-
id="emoji-search"
198
-
class="emoji-search"
199
-
placeholder="Search emojis..."
200
-
autocomplete="off">
201
-
</div>
202
-
<div class="emoji-categories" id="emoji-categories">
203
-
<button class="category-btn active" data-category="frequent">Frequent</button>
204
-
<button class="category-btn" data-category="custom">Custom</button>
205
-
<button class="category-btn" data-category="people">People</button>
206
-
<button class="category-btn" data-category="nature">Nature</button>
207
-
<button class="category-btn" data-category="food">Food</button>
208
-
<button class="category-btn" data-category="activity">Activity</button>
209
-
<button class="category-btn" data-category="travel">Travel</button>
210
-
<button class="category-btn" data-category="objects">Objects</button>
211
-
<button class="category-btn" data-category="symbols">Symbols</button>
212
-
<button class="category-btn" data-category="flags">Flags</button>
213
-
</div>
214
-
<div class="emoji-grid" id="emoji-grid">
215
-
<!-- Will be populated by JavaScript -->
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
216
</div>
217
</div>
218
···
455
<style>
456
body {
457
font-family: var(--font-family) !important;
0
0
0
0
458
}
459
460
:root {
···
1049
}
1050
1051
/* Emoji Picker */
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1052
.emoji-picker {
1053
-
position: absolute;
0
1054
background: var(--bg-secondary);
1055
border: 1px solid var(--border-color);
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1056
border-radius: var(--radius);
0
1057
padding: 1rem;
1058
-
box-shadow: var(--shadow-md);
1059
-
width: 320px;
1060
-
max-height: 400px;
1061
-
overflow-y: auto;
1062
-
z-index: 1000;
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1063
}
1064
1065
.emoji-search-container {
1066
-
margin-bottom: 1rem;
1067
}
1068
1069
.emoji-search {
1070
width: 100%;
1071
-
padding: 0.5rem 0.75rem;
1072
background: var(--bg-primary);
1073
border: 1px solid var(--border-color);
1074
-
border-radius: var(--radius-sm);
1075
color: var(--text-primary);
1076
-
font-size: 0.875rem;
1077
outline: none;
1078
-
transition: border-color 0.2s;
1079
}
1080
1081
.emoji-search:focus {
1082
border-color: var(--accent);
1083
-
box-shadow: 0 0 0 2px rgba(74, 158, 255, 0.1);
1084
}
1085
1086
.emoji-search::placeholder {
1087
color: var(--text-tertiary);
1088
}
1089
0
0
0
0
0
0
0
0
1090
.emoji-categories {
1091
display: flex;
1092
-
gap: 0.25rem;
1093
-
margin-bottom: 1rem;
1094
overflow-x: auto;
1095
padding-bottom: 0.5rem;
0
1096
}
1097
1098
.emoji-categories.hidden {
···
1101
1102
.category-btn {
1103
background: transparent;
1104
-
border: none;
1105
color: var(--text-secondary);
1106
-
font-size: 0.75rem;
1107
-
padding: 0.25rem 0.5rem;
1108
cursor: pointer;
1109
white-space: nowrap;
1110
-
border-radius: var(--radius-sm);
1111
-
transition: all 0.2s;
1112
}
1113
1114
-
.category-btn:hover {
1115
-
background: var(--bg-tertiary);
0
0
0
1116
}
1117
1118
.category-btn.active {
1119
background: var(--accent);
1120
color: white;
0
1121
}
1122
1123
.emoji-grid {
0
1124
display: grid;
1125
-
grid-template-columns: repeat(auto-fill, 2rem);
1126
-
gap: 0.25rem;
1127
-
justify-content: start;
0
1128
}
1129
1130
.emoji-option {
1131
-
background: transparent;
1132
-
border: none;
1133
-
font-size: 1.5rem;
1134
-
padding: 0.25rem;
1135
cursor: pointer;
1136
-
border-radius: var(--radius-sm);
1137
-
transition: background 0.2s;
1138
display: flex;
1139
align-items: center;
1140
justify-content: center;
1141
-
width: 2rem;
1142
-
height: 2rem;
0
1143
}
1144
1145
-
.emoji-option:hover {
0
1146
background: var(--bg-tertiary);
0
0
0
1147
}
1148
1149
-
/* Custom emoji styles */
1150
-
.emoji-option.custom-emoji {
1151
-
padding: 0.125rem;
0
1152
}
1153
1154
-
.emoji-option.custom-emoji img {
1155
-
width: 1.75rem;
1156
-
height: 1.75rem;
1157
-
object-fit: contain;
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1158
}
1159
1160
/* Clear Picker */
···
1937
1938
// Emoji picker
1939
const emojiTrigger = document.getElementById('emoji-trigger');
0
1940
const emojiPicker = document.getElementById('emoji-picker');
0
1941
const emojiGrid = document.getElementById('emoji-grid');
1942
const selectedEmoji = document.getElementById('selected-emoji');
1943
const statusInput = document.getElementById('status-input');
0
0
0
1944
1945
// Clear time picker
1946
const clearAfterBtn = document.getElementById('clear-after-btn');
···
1948
const clearText = document.getElementById('clear-text');
1949
const expiresSelect = document.getElementById('expires_in');
1950
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1951
// Show emoji picker
1952
-
if (emojiTrigger && emojiPicker) {
1953
emojiTrigger.addEventListener('click', (e) => {
0
1954
e.stopPropagation();
1955
-
const wasHidden = emojiPicker.style.display === 'none';
1956
-
emojiPicker.style.display = wasHidden ? 'block' : 'none';
1957
-
clearPicker.style.display = 'none';
1958
-
1959
-
if (wasHidden) {
1960
-
// Position picker
1961
-
const rect = emojiTrigger.getBoundingClientRect();
1962
-
emojiPicker.style.top = rect.bottom + 'px';
1963
-
emojiPicker.style.left = rect.left + 'px';
1964
-
1965
-
// Clear search and show categories
1966
-
const emojiSearch = document.getElementById('emoji-search');
1967
-
if (emojiSearch) {
1968
-
emojiSearch.value = '';
1969
-
document.getElementById('emoji-categories').classList.remove('hidden');
1970
-
}
1971
-
1972
-
// Load frequent emojis by default (wait for custom emojis to be loaded first)
1973
-
loadCustomEmojis().then(() => {
1974
-
loadEmojiCategory('frequent');
1975
-
});
1976
-
1977
-
// Focus search for easy typing
1978
-
setTimeout(() => {
1979
-
if (emojiSearch) emojiSearch.focus();
1980
-
}, 50);
1981
}
1982
});
1983
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1984
1985
// Store custom emojis
1986
let customEmojis = [];
···
2003
document.addEventListener('custom-emoji-uploaded', async (e) => {
2004
await loadCustomEmojis();
2005
// If picker visible, refresh current category
2006
-
if (emojiPicker && emojiPicker.style.display !== 'none') {
2007
const active = document.querySelector('.emoji-categories .category-btn.active');
2008
const cat = active ? active.getAttribute('data-category') : 'frequent';
2009
loadEmojiCategory(cat || 'frequent');
···
2050
}
2051
2052
statusInput.value = emoji;
2053
-
emojiPicker.style.display = 'none';
0
2054
checkForChanges();
2055
});
2056
});
···
2077
// Display the image in the selected emoji area
2078
selectedEmoji.innerHTML = `<img src="${img.src}" alt="${img.alt}" style="width: 100%; height: 100%; object-fit: contain;">`;
2079
statusInput.value = emojiValue;
2080
-
emojiPicker.style.display = 'none';
0
2081
checkForChanges();
2082
});
2083
});
···
2095
const emoji = e.target.getAttribute('data-emoji');
2096
selectedEmoji.textContent = emoji;
2097
statusInput.value = emoji;
2098
-
emojiPicker.style.display = 'none';
0
2099
checkForChanges();
2100
});
2101
});
···
2119
clearAfterBtn.addEventListener('click', (e) => {
2120
e.stopPropagation();
2121
clearPicker.style.display = clearPicker.style.display === 'none' ? 'block' : 'none';
2122
-
emojiPicker.style.display = 'none';
2123
2124
// Position picker
2125
const rect = clearAfterBtn.getBoundingClientRect();
···
2228
2229
// Close pickers on outside click
2230
document.addEventListener('click', (e) => {
2231
-
if (!emojiPicker.contains(e.target) && e.target !== emojiTrigger) {
2232
-
emojiPicker.style.display = 'none';
2233
-
}
2234
-
if (!clearPicker.contains(e.target) && e.target !== clearAfterBtn) {
2235
clearPicker.style.display = 'none';
2236
}
2237
});
···
2331
}
2332
2333
statusInput.value = emojiValue;
2334
-
emojiPicker.style.display = 'none';
0
2335
checkForChanges();
2336
// Clear search when emoji is selected
2337
document.getElementById('emoji-search').value = '';
···
190
</form>
191
</div>
192
193
+
<!-- Emoji Picker Modal -->
194
+
<div class="emoji-picker-overlay hidden" id="emoji-picker-overlay" aria-hidden="true">
195
+
<div class="emoji-picker" id="emoji-picker" role="dialog" aria-modal="true" aria-labelledby="emoji-picker-title">
196
+
<div class="emoji-picker-header">
197
+
<div>
198
+
<h2 id="emoji-picker-title">pick an emoji</h2>
199
+
<p class="emoji-picker-subtitle">custom and unicode options side by side</p>
200
+
</div>
201
+
<button type="button" class="emoji-picker-close" id="emoji-picker-close" aria-label="close emoji picker">✕</button>
202
+
</div>
203
+
<div class="emoji-picker-preview">
204
+
<div class="emoji-preview-block">
205
+
<span class="emoji-preview-label">current</span>
206
+
<div class="emoji-preview-emoji" id="emoji-preview-current">
207
+
{% if let Some(current) = current_status.as_ref() %}
208
+
{% if current.status.starts_with("custom:") %}
209
+
{% let emoji_name = current.status.strip_prefix("custom:").unwrap() %}
210
+
<img src="/emojis/{{emoji_name}}.png" alt="{{emoji_name}}" title="{{emoji_name}}"
211
+
onerror="this.onerror=null; this.src='/emojis/{{emoji_name}}.gif';">
212
+
{% else %}
213
+
<span title="{{ current.status }}">{{ current.status }}</span>
214
+
{% endif %}
215
+
{% else %}
216
+
<span title="happy">😊</span>
217
+
{% endif %}
218
+
</div>
219
+
</div>
220
+
<div class="emoji-preview-block">
221
+
<span class="emoji-preview-label">next</span>
222
+
<div class="emoji-preview-emoji" id="emoji-preview-next">
223
+
{% if let Some(current) = current_status.as_ref() %}
224
+
{% if current.status.starts_with("custom:") %}
225
+
{% let emoji_name = current.status.strip_prefix("custom:").unwrap() %}
226
+
<img src="/emojis/{{emoji_name}}.png" alt="{{emoji_name}}" title="{{emoji_name}}"
227
+
onerror="this.onerror=null; this.src='/emojis/{{emoji_name}}.gif';">
228
+
{% else %}
229
+
<span title="{{ current.status }}">{{ current.status }}</span>
230
+
{% endif %}
231
+
{% else %}
232
+
<span title="happy">😊</span>
233
+
{% endif %}
234
+
</div>
235
+
</div>
236
+
</div>
237
+
<div class="emoji-search-container">
238
+
<input type="text"
239
+
id="emoji-search"
240
+
class="emoji-search"
241
+
placeholder="Search emojis..."
242
+
autocomplete="off">
243
+
</div>
244
+
<div class="emoji-picker-body">
245
+
<div class="emoji-categories" id="emoji-categories">
246
+
<button class="category-btn active" data-category="frequent">Frequent</button>
247
+
<button class="category-btn" data-category="custom">Custom</button>
248
+
<button class="category-btn" data-category="people">People</button>
249
+
<button class="category-btn" data-category="nature">Nature</button>
250
+
<button class="category-btn" data-category="food">Food</button>
251
+
<button class="category-btn" data-category="activity">Activity</button>
252
+
<button class="category-btn" data-category="travel">Travel</button>
253
+
<button class="category-btn" data-category="objects">Objects</button>
254
+
<button class="category-btn" data-category="symbols">Symbols</button>
255
+
<button class="category-btn" data-category="flags">Flags</button>
256
+
</div>
257
+
<div class="emoji-grid" id="emoji-grid">
258
+
<!-- Will be populated by JavaScript -->
259
+
</div>
260
+
</div>
261
</div>
262
</div>
263
···
500
<style>
501
body {
502
font-family: var(--font-family) !important;
503
+
}
504
+
505
+
body.modal-open {
506
+
overflow: hidden;
507
}
508
509
:root {
···
1098
}
1099
1100
/* Emoji Picker */
1101
+
.emoji-picker-overlay {
1102
+
position: fixed;
1103
+
inset: 0;
1104
+
background: rgba(6, 6, 8, 0.75);
1105
+
backdrop-filter: blur(6px);
1106
+
display: flex;
1107
+
align-items: center;
1108
+
justify-content: center;
1109
+
padding: clamp(1rem, 6vw, 2.5rem);
1110
+
z-index: 1400;
1111
+
}
1112
+
1113
+
.emoji-picker-overlay.hidden {
1114
+
display: none;
1115
+
}
1116
+
1117
.emoji-picker {
1118
+
width: min(720px, 100%);
1119
+
max-height: min(90vh, 720px);
1120
background: var(--bg-secondary);
1121
border: 1px solid var(--border-color);
1122
+
border-radius: clamp(var(--radius), 2vw, 24px);
1123
+
box-shadow: 0 32px 80px rgba(0, 0, 0, 0.45);
1124
+
display: flex;
1125
+
flex-direction: column;
1126
+
gap: 1rem;
1127
+
padding: clamp(1.25rem, 5vw, 2rem);
1128
+
overflow: hidden;
1129
+
}
1130
+
1131
+
.emoji-picker-header {
1132
+
display: flex;
1133
+
align-items: flex-start;
1134
+
justify-content: space-between;
1135
+
gap: 1rem;
1136
+
}
1137
+
1138
+
.emoji-picker-header h2 {
1139
+
margin: 0;
1140
+
font-size: 1.5rem;
1141
+
color: var(--text-primary);
1142
+
}
1143
+
1144
+
.emoji-picker-subtitle {
1145
+
margin: 0.25rem 0 0;
1146
+
font-size: 0.875rem;
1147
+
color: var(--text-tertiary);
1148
+
}
1149
+
1150
+
.emoji-picker-close {
1151
+
border: 1px solid var(--border-color);
1152
+
background: var(--bg-tertiary);
1153
+
color: var(--text-secondary);
1154
+
border-radius: 999px;
1155
+
width: 2.25rem;
1156
+
height: 2.25rem;
1157
+
display: flex;
1158
+
align-items: center;
1159
+
justify-content: center;
1160
+
cursor: pointer;
1161
+
transition: all 0.2s ease;
1162
+
}
1163
+
1164
+
.emoji-picker-close:hover,
1165
+
.emoji-picker-close:focus-visible {
1166
+
color: white;
1167
+
background: var(--accent);
1168
+
border-color: transparent;
1169
+
outline: none;
1170
+
}
1171
+
1172
+
.emoji-picker-preview {
1173
+
display: flex;
1174
+
gap: 1rem;
1175
+
background: var(--bg-primary);
1176
border-radius: var(--radius);
1177
+
border: 1px solid var(--border-color);
1178
padding: 1rem;
1179
+
}
1180
+
1181
+
.emoji-preview-block {
1182
+
flex: 1;
1183
+
display: flex;
1184
+
flex-direction: column;
1185
+
align-items: center;
1186
+
gap: 0.5rem;
1187
+
}
1188
+
1189
+
.emoji-preview-label {
1190
+
font-size: 0.75rem;
1191
+
text-transform: uppercase;
1192
+
letter-spacing: 0.08em;
1193
+
color: var(--text-tertiary);
1194
+
}
1195
+
1196
+
.emoji-preview-emoji {
1197
+
width: clamp(3.25rem, 16vw, 4.5rem);
1198
+
height: clamp(3.25rem, 16vw, 4.5rem);
1199
+
display: flex;
1200
+
align-items: center;
1201
+
justify-content: center;
1202
+
font-size: clamp(2rem, 10vw, 3rem);
1203
+
border-radius: var(--radius);
1204
+
background: var(--bg-secondary);
1205
+
border: 1px solid rgba(255, 255, 255, 0.05);
1206
+
overflow: hidden;
1207
+
}
1208
+
1209
+
.emoji-preview-emoji img {
1210
+
width: 100%;
1211
+
height: 100%;
1212
+
object-fit: contain;
1213
+
}
1214
+
1215
+
.emoji-preview-emoji span {
1216
+
display: block;
1217
}
1218
1219
.emoji-search-container {
1220
+
margin: 0;
1221
}
1222
1223
.emoji-search {
1224
width: 100%;
1225
+
padding: 0.75rem 1rem;
1226
background: var(--bg-primary);
1227
border: 1px solid var(--border-color);
1228
+
border-radius: var(--radius);
1229
color: var(--text-primary);
1230
+
font-size: 0.95rem;
1231
outline: none;
1232
+
transition: border-color 0.2s ease, box-shadow 0.2s ease;
1233
}
1234
1235
.emoji-search:focus {
1236
border-color: var(--accent);
1237
+
box-shadow: 0 0 0 2px rgba(74, 158, 255, 0.12);
1238
}
1239
1240
.emoji-search::placeholder {
1241
color: var(--text-tertiary);
1242
}
1243
1244
+
.emoji-picker-body {
1245
+
flex: 1;
1246
+
display: flex;
1247
+
flex-direction: column;
1248
+
gap: 0.75rem;
1249
+
min-height: 0;
1250
+
}
1251
+
1252
.emoji-categories {
1253
display: flex;
1254
+
gap: 0.5rem;
0
1255
overflow-x: auto;
1256
padding-bottom: 0.5rem;
1257
+
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
1258
}
1259
1260
.emoji-categories.hidden {
···
1263
1264
.category-btn {
1265
background: transparent;
1266
+
border: 1px solid transparent;
1267
color: var(--text-secondary);
1268
+
font-size: 0.8rem;
1269
+
padding: 0.4rem 0.75rem;
1270
cursor: pointer;
1271
white-space: nowrap;
1272
+
border-radius: 999px;
1273
+
transition: all 0.2s ease;
1274
}
1275
1276
+
.category-btn:hover,
1277
+
.category-btn:focus-visible {
1278
+
border-color: var(--accent);
1279
+
color: var(--accent);
1280
+
outline: none;
1281
}
1282
1283
.category-btn.active {
1284
background: var(--accent);
1285
color: white;
1286
+
border-color: transparent;
1287
}
1288
1289
.emoji-grid {
1290
+
flex: 1;
1291
display: grid;
1292
+
grid-template-columns: repeat(auto-fill, minmax(56px, 1fr));
1293
+
gap: 0.5rem;
1294
+
padding-right: 0.25rem;
1295
+
overflow-y: auto;
1296
}
1297
1298
.emoji-option {
1299
+
background: var(--bg-primary);
1300
+
border: 1px solid transparent;
1301
+
font-size: 2rem;
0
1302
cursor: pointer;
1303
+
border-radius: 16px;
1304
+
transition: transform 0.15s ease, border-color 0.2s ease, background 0.2s ease;
1305
display: flex;
1306
align-items: center;
1307
justify-content: center;
1308
+
width: 100%;
1309
+
aspect-ratio: 1;
1310
+
position: relative;
1311
}
1312
1313
+
.emoji-option:hover,
1314
+
.emoji-option:focus-visible {
1315
background: var(--bg-tertiary);
1316
+
border-color: var(--accent);
1317
+
transform: translateY(-2px);
1318
+
outline: none;
1319
}
1320
1321
+
.emoji-option.custom-emoji img {
1322
+
width: 70%;
1323
+
height: 70%;
1324
+
object-fit: contain;
1325
}
1326
1327
+
.emoji-option.custom-emoji::after {
1328
+
content: '';
1329
+
position: absolute;
1330
+
inset: 8%;
1331
+
border-radius: 12px;
1332
+
pointer-events: none;
1333
+
}
1334
+
1335
+
@media (max-width: 640px) {
1336
+
.emoji-picker-overlay {
1337
+
padding: 0;
1338
+
align-items: stretch;
1339
+
}
1340
+
1341
+
.emoji-picker {
1342
+
width: 100%;
1343
+
height: 100%;
1344
+
max-height: none;
1345
+
border-radius: 0;
1346
+
padding: 1.25rem 1rem 1rem;
1347
+
gap: 0.75rem;
1348
+
}
1349
+
1350
+
.emoji-picker-preview {
1351
+
padding: 0.75rem;
1352
+
}
1353
+
1354
+
.emoji-grid {
1355
+
grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
1356
+
}
1357
}
1358
1359
/* Clear Picker */
···
2136
2137
// Emoji picker
2138
const emojiTrigger = document.getElementById('emoji-trigger');
2139
+
const emojiPickerOverlay = document.getElementById('emoji-picker-overlay');
2140
const emojiPicker = document.getElementById('emoji-picker');
2141
+
const emojiPickerClose = document.getElementById('emoji-picker-close');
2142
const emojiGrid = document.getElementById('emoji-grid');
2143
const selectedEmoji = document.getElementById('selected-emoji');
2144
const statusInput = document.getElementById('status-input');
2145
+
const emojiPreviewCurrent = document.getElementById('emoji-preview-current');
2146
+
const emojiPreviewNext = document.getElementById('emoji-preview-next');
2147
+
let lastFocusBeforeEmojiPicker = null;
2148
2149
// Clear time picker
2150
const clearAfterBtn = document.getElementById('clear-after-btn');
···
2152
const clearText = document.getElementById('clear-text');
2153
const expiresSelect = document.getElementById('expires_in');
2154
2155
+
const isEmojiPickerOpen = () => emojiPickerOverlay && !emojiPickerOverlay.classList.contains('hidden');
2156
+
2157
+
const syncPreviewWithSelection = () => {
2158
+
if (emojiPreviewNext && selectedEmoji) {
2159
+
emojiPreviewNext.innerHTML = selectedEmoji.innerHTML;
2160
+
}
2161
+
};
2162
+
2163
+
syncPreviewWithSelection();
2164
+
2165
+
const openEmojiPicker = () => {
2166
+
if (!emojiPickerOverlay || !emojiPicker) return;
2167
+
2168
+
lastFocusBeforeEmojiPicker = document.activeElement;
2169
+
2170
+
emojiPickerOverlay.classList.remove('hidden');
2171
+
emojiPickerOverlay.setAttribute('aria-hidden', 'false');
2172
+
document.body.classList.add('modal-open');
2173
+
if (clearPicker) clearPicker.style.display = 'none';
2174
+
2175
+
const emojiSearch = document.getElementById('emoji-search');
2176
+
if (emojiSearch) {
2177
+
emojiSearch.value = '';
2178
+
const categories = document.getElementById('emoji-categories');
2179
+
if (categories) categories.classList.remove('hidden');
2180
+
setTimeout(() => {
2181
+
if (emojiSearch) emojiSearch.focus();
2182
+
}, 60);
2183
+
}
2184
+
2185
+
if (emojiPreviewCurrent) {
2186
+
const currentDisplay = document.querySelector('.current-status .status-emoji');
2187
+
if (currentDisplay) {
2188
+
emojiPreviewCurrent.innerHTML = currentDisplay.innerHTML;
2189
+
} else if (selectedEmoji) {
2190
+
emojiPreviewCurrent.innerHTML = selectedEmoji.innerHTML;
2191
+
}
2192
+
}
2193
+
2194
+
syncPreviewWithSelection();
2195
+
2196
+
loadCustomEmojis().then(() => {
2197
+
loadEmojiCategory('frequent');
2198
+
});
2199
+
};
2200
+
2201
+
const closeEmojiPicker = () => {
2202
+
if (!emojiPickerOverlay) return;
2203
+
emojiPickerOverlay.classList.add('hidden');
2204
+
emojiPickerOverlay.setAttribute('aria-hidden', 'true');
2205
+
document.body.classList.remove('modal-open');
2206
+
if (lastFocusBeforeEmojiPicker && typeof lastFocusBeforeEmojiPicker.focus === 'function') {
2207
+
const focusTarget = lastFocusBeforeEmojiPicker;
2208
+
setTimeout(() => {
2209
+
try { focusTarget.focus(); } catch (_) {}
2210
+
}, 50);
2211
+
}
2212
+
lastFocusBeforeEmojiPicker = null;
2213
+
};
2214
+
2215
// Show emoji picker
2216
+
if (emojiTrigger && emojiPickerOverlay) {
2217
emojiTrigger.addEventListener('click', (e) => {
2218
+
e.preventDefault();
2219
e.stopPropagation();
2220
+
if (isEmojiPickerOpen()) {
2221
+
closeEmojiPicker();
2222
+
} else {
2223
+
openEmojiPicker();
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
2224
}
2225
});
2226
}
2227
+
2228
+
if (emojiPickerClose) {
2229
+
emojiPickerClose.addEventListener('click', (e) => {
2230
+
e.preventDefault();
2231
+
closeEmojiPicker();
2232
+
});
2233
+
}
2234
+
2235
+
if (emojiPickerOverlay) {
2236
+
emojiPickerOverlay.addEventListener('click', (e) => {
2237
+
if (e.target === emojiPickerOverlay) {
2238
+
closeEmojiPicker();
2239
+
}
2240
+
});
2241
+
}
2242
+
2243
+
document.addEventListener('keydown', (e) => {
2244
+
if (e.key === 'Escape' && isEmojiPickerOpen()) {
2245
+
closeEmojiPicker();
2246
+
}
2247
+
});
2248
2249
// Store custom emojis
2250
let customEmojis = [];
···
2267
document.addEventListener('custom-emoji-uploaded', async (e) => {
2268
await loadCustomEmojis();
2269
// If picker visible, refresh current category
2270
+
if (isEmojiPickerOpen()) {
2271
const active = document.querySelector('.emoji-categories .category-btn.active');
2272
const cat = active ? active.getAttribute('data-category') : 'frequent';
2273
loadEmojiCategory(cat || 'frequent');
···
2314
}
2315
2316
statusInput.value = emoji;
2317
+
syncPreviewWithSelection();
2318
+
closeEmojiPicker();
2319
checkForChanges();
2320
});
2321
});
···
2342
// Display the image in the selected emoji area
2343
selectedEmoji.innerHTML = `<img src="${img.src}" alt="${img.alt}" style="width: 100%; height: 100%; object-fit: contain;">`;
2344
statusInput.value = emojiValue;
2345
+
syncPreviewWithSelection();
2346
+
closeEmojiPicker();
2347
checkForChanges();
2348
});
2349
});
···
2361
const emoji = e.target.getAttribute('data-emoji');
2362
selectedEmoji.textContent = emoji;
2363
statusInput.value = emoji;
2364
+
syncPreviewWithSelection();
2365
+
closeEmojiPicker();
2366
checkForChanges();
2367
});
2368
});
···
2386
clearAfterBtn.addEventListener('click', (e) => {
2387
e.stopPropagation();
2388
clearPicker.style.display = clearPicker.style.display === 'none' ? 'block' : 'none';
2389
+
closeEmojiPicker();
2390
2391
// Position picker
2392
const rect = clearAfterBtn.getBoundingClientRect();
···
2495
2496
// Close pickers on outside click
2497
document.addEventListener('click', (e) => {
2498
+
if (clearPicker && !clearPicker.contains(e.target) && e.target !== clearAfterBtn) {
0
0
0
2499
clearPicker.style.display = 'none';
2500
}
2501
});
···
2595
}
2596
2597
statusInput.value = emojiValue;
2598
+
syncPreviewWithSelection();
2599
+
closeEmojiPicker();
2600
checkForChanges();
2601
// Clear search when emoji is selected
2602
document.getElementById('emoji-search').value = '';