Улучшения UI чата: круглые кнопки, emoji-picker, исправление WebSocket переподключения

- Сделаны круглыми кнопки отправки и прокрутки вниз
- Убран фон у поля ввода сообщений
- Добавлен emoji-picker-element с поддержкой категорий
- Скрыта кнопка выбора тона кожи в emoji picker
- Изменена иконка кнопки отправки на ракету
- Исправлена бесконечная перезагрузка при падении WebSocket
- Добавлено умное переподключение WebSocket с 5 попытками
- Улучшена анимация кнопки прокрутки вниз
This commit is contained in:
admin i 2025-11-28 13:07:17 +03:00
parent 0b3a633290
commit 67f118a1df
8 changed files with 581 additions and 35 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -60,6 +60,7 @@ class ChatComposer {
}
this.bindEvents();
this.bindEmojiPicker();
form.dataset.composerBound = '1';
}
@ -95,6 +96,42 @@ class ChatComposer {
window.addEventListener('chat:message-received', this.boundMessageHandler);
}
bindEmojiPicker() {
// Новый emoji-picker-element
const picker = this.form.querySelector('[data-emoji-picker]');
if (picker) {
picker.addEventListener('emoji-click', (event) => {
event.preventDefault();
const emoji = event.detail?.unicode;
if (emoji) {
this.insertEmoji(emoji);
}
});
// Предотвращаем закрытие dropdown при клике внутри emoji-picker
const emojiDropdown = picker.closest('.dropdown-menu');
if (emojiDropdown) {
emojiDropdown.addEventListener('click', (event) => {
event.stopPropagation();
});
}
}
}
insertEmoji(emoji) {
if (!this.textarea) return;
const textarea = this.textarea;
const start = textarea.selectionStart ?? textarea.value.length;
const end = textarea.selectionEnd ?? textarea.value.length;
const value = textarea.value || '';
const emojiText = typeof emoji === 'string' ? emoji : emoji.emoji || emoji.unicode || emoji;
textarea.value = `${value.slice(0, start)}${emojiText}${value.slice(end)}`;
const caret = start + emojiText.length;
textarea.focus();
textarea.setSelectionRange?.(caret, caret);
textarea.dispatchEvent(new Event('input', { bubbles: true }));
}
handleFileSelect(event) {
const files = Array.from(event.target.files || []);
if (!files.length) return;

View File

@ -27,6 +27,11 @@ import {
* @param {Object} [options.markReadApi] - API из chatMarkRead для интеграции
* @returns {Object|null} API WebSocket или null если параметры невалидны
*/
// Храним активное соединение для предотвращения дубликатов
let activeWebSocket = null;
let lastOptions = null;
export function initChatWebSocket(options = {}) {
const {
chatId,
@ -47,6 +52,15 @@ export function initChatWebSocket(options = {}) {
console.warn('ChatWebSocket: chatId and meId are required');
return null;
}
// Закрываем существующее соединение перед созданием нового
if (activeWebSocket && activeWebSocket.readyState !== WebSocket.CLOSED) {
console.log('ChatWebSocket: closing existing connection');
activeWebSocket.close(1000, 'Reconnecting');
}
// Сохраняем опции для переподключения
lastOptions = options;
// Получение элементов
const scrollEl = document.getElementById(scrollContainerId);
@ -66,6 +80,14 @@ export function initChatWebSocket(options = {}) {
const proto = location.protocol === 'https:' ? 'wss' : 'ws';
const wsUrl = `${proto}://${location.host}/ws/chat/${chatId}/`;
const ws = new WebSocket(wsUrl);
// Сохраняем активное соединение
activeWebSocket = ws;
// Настройки переподключения
let reconnectAttempts = 0;
const maxReconnectAttempts = 5;
const reconnectDelay = 3000;
/**
* Инициализирует последний день из DOM
@ -165,6 +187,14 @@ export function initChatWebSocket(options = {}) {
}, 2000);
}
/**
* Обрабатывает успешное подключение
*/
ws.addEventListener('open', () => {
console.log('ChatWebSocket: connection established');
reconnectAttempts = 0; // Сбрасываем счетчик при успешном подключении
});
/**
* Обрабатывает входящие сообщения WebSocket
*/
@ -189,9 +219,40 @@ export function initChatWebSocket(options = {}) {
/**
* Обрабатывает закрытие соединения
*/
ws.addEventListener('close', () => {
console.log('ChatWebSocket: connection closed, reloading in 2s...');
setTimeout(() => location.reload(), 2000);
ws.addEventListener('close', (event) => {
console.log('ChatWebSocket: connection closed', event.code, event.reason);
// Не переподключаемся если закрытие было нормальным (код 1000)
if (event.code === 1000) {
console.log('ChatWebSocket: normal closure, not reconnecting');
return;
}
// Пробуем переподключиться
if (reconnectAttempts < maxReconnectAttempts) {
reconnectAttempts++;
console.log(`ChatWebSocket: reconnecting in ${reconnectDelay}ms (attempt ${reconnectAttempts}/${maxReconnectAttempts})`);
setTimeout(() => {
if (document.querySelector('[data-chat-ws]')) {
console.log('ChatWebSocket: attempting to reconnect...');
// Переинициализируем WebSocket с сохраненными опциями
initChatWebSocket(lastOptions);
}
}, reconnectDelay);
} else {
console.warn('ChatWebSocket: max reconnect attempts reached');
// Показываем уведомление пользователю вместо перезагрузки
const notification = document.createElement('div');
notification.className = 'alert alert-warning position-fixed bottom-0 start-50 translate-middle-x mb-3';
notification.style.zIndex = '9999';
notification.innerHTML = `
<i class="bi-exclamation-triangle me-2"></i>
Соединение с сервером потеряно.
<button class="btn btn-sm btn-link" onclick="location.reload()">Обновить страницу</button>
`;
document.body.appendChild(notification);
}
});
/**
@ -199,6 +260,7 @@ export function initChatWebSocket(options = {}) {
*/
ws.addEventListener('error', (err) => {
console.error('ChatWebSocket: connection error', err);
// Не перезагружаем страницу при ошибке - дожидаемся события 'close'
});
/**

View File

@ -0,0 +1,343 @@
/**
* Русские ключевые слова для поиска эмодзи
* Формат: emoji -> массив русских синонимов
*/
export const emojiRussianKeywords = {
// Смайлики и эмоции
'😀': ['улыбка', 'радость', 'счастье', 'смайлик'],
'😃': ['улыбка', 'радость', 'смайлик'],
'😄': ['смех', 'улыбка', 'радость'],
'😁': ['ухмылка', 'улыбка', 'зубы'],
'😆': ['смех', 'хохот', 'веселье'],
'😅': ['пот', 'облегчение', 'смех'],
'🤣': ['смех', 'хохот', 'ржу'],
'😂': ['слезы', 'смех', 'плакать от смеха'],
'🙂': ['улыбка', 'легкая улыбка'],
'🙃': ['перевернутый', 'сарказм'],
'😉': ['подмигивание', 'намек'],
'😊': ['застенчивый', 'милый', 'улыбка'],
'😇': ['ангел', 'святой', 'невинный'],
'🥰': ['любовь', 'влюблен', 'сердечки'],
'😍': ['любовь', 'влюблен', 'сердце в глазах'],
'🤩': ['звезды', 'восторг', 'вау'],
'😘': ['поцелуй', 'воздушный поцелуй'],
'😗': ['поцелуй', 'губы'],
'😚': ['поцелуй', 'закрытые глаза'],
'😙': ['поцелуй', 'улыбка'],
'🥲': ['слеза', 'трогательно', 'счастье со слезой'],
'😋': ['вкусно', 'язык', 'ням'],
'😛': ['язык', 'дразнить'],
'😜': ['подмигивание', 'язык', 'шутка'],
'🤪': ['безумный', 'сумасшедший', 'веселье'],
'😝': ['язык', 'закрытые глаза'],
'🤑': ['деньги', 'богатство', 'жадность'],
'🤗': ['объятие', 'обнять'],
'🤭': ['ой', 'упс', 'смущение'],
'🤫': ['тсс', 'тихо', 'секрет'],
'🤔': ['думать', 'размышление', 'хм'],
'🤐': ['молчание', 'замок на губах'],
'🤨': ['подозрение', 'скептицизм', 'бровь'],
'😐': ['нейтральный', 'без эмоций'],
'😑': ['без эмоций', 'недовольство'],
'😶': ['без рта', 'молчание'],
'😏': ['ухмылка', 'самодовольство'],
'😒': ['недовольство', 'скука'],
'🙄': ['закатить глаза', 'раздражение'],
'😬': ['неловко', 'смущение', 'зубы'],
'😮': ['удивление', 'шок', 'о'],
'😯': ['удивление', 'ошеломление'],
'😲': ['шок', 'удивление'],
'😳': ['смущение', 'покраснение'],
'🥺': ['умоляющий', 'жалость', 'щенячьи глаза'],
'😦': ['нахмуренный', 'озабоченность'],
'😧': ['тревога', 'страх'],
'😨': ['испуг', 'страх'],
'😰': ['тревога', 'пот', 'нервы'],
'😥': ['грусть', 'облегчение', 'пот'],
'😢': ['слезы', 'плач', 'грусть'],
'😭': ['рыдание', 'плач', 'слезы'],
'😱': ['крик', 'ужас', 'страх'],
'😖': ['смущение', 'разочарование'],
'😣': ['упорство', 'настойчивость'],
'😞': ['разочарование', 'грусть'],
'😓': ['усталость', 'пот', 'стресс'],
'😩': ['усталость', 'стон'],
'😫': ['устал', 'измучен'],
'🥱': ['зевок', 'усталость', 'скука'],
'😤': ['триумф', 'гордость', 'пар из носа'],
'😡': ['злость', 'гнев'],
'😠': ['злой', 'гневный'],
'🤬': ['ругань', 'мат', 'злость'],
'😈': ['дьявол', 'зло', 'рога'],
'👿': ['дьявол', 'злой', 'рога'],
'💀': ['череп', 'смерть', 'умираю'],
'☠️': ['череп', 'яд', 'опасность'],
// Жесты
'👋': ['привет', 'пока', 'махать рукой'],
'🤚': ['рука', 'стоп'],
'🖐️': ['рука', 'пять', 'ладонь'],
'✋': ['стоп', 'рука', 'ладонь'],
'🖖': ['вулканский привет', 'спок'],
'👌': ['ок', 'окей', 'отлично'],
'🤌': ['жест', 'итальянский'],
'🤏': ['маленький', 'чуть-чуть'],
'✌️': ['победа', 'пис', 'два'],
'🤞': ['скрестить пальцы', 'удача'],
'🤟': ['люблю тебя', 'рок'],
'🤘': ['рок', 'рога'],
'🤙': ['позвони', 'телефон'],
'👈': ['влево', 'указать'],
'👉': ['вправо', 'указать'],
'👆': ['вверх', 'указать'],
'👇': ['вниз', 'указать'],
'☝️': ['вверх', 'один'],
'👍': ['лайк', 'класс', 'отлично'],
'👎': ['дизлайк', 'плохо'],
'✊': ['кулак', 'сила'],
'👊': ['кулак', 'удар'],
'🤛': ['кулак', 'удар слева'],
'🤜': ['кулак', 'удар справа'],
'👏': ['аплодисменты', 'хлопать'],
'🙌': ['руки вверх', 'ура', 'праздник'],
'👐': ['открытые руки', 'объятие'],
'🤲': ['молитва', 'просьба'],
'🤝': ['рукопожатие', 'согласие', 'сделка'],
'🙏': ['молитва', 'спасибо', 'пожалуйста'],
// Сердца и любовь
'💘': ['любовь', 'сердце', 'стрела'],
'💝': ['подарок', 'сердце'],
'💖': ['блестящее сердце', 'любовь'],
'💗': ['растущее сердце', 'любовь'],
'💓': ['бьющееся сердце', 'любовь'],
'💞': ['вращающиеся сердца', 'любовь'],
'💕': ['два сердца', 'любовь'],
'💟': ['сердце', 'украшение'],
'❣️': ['восклицание', 'сердце'],
'💔': ['разбитое сердце', 'грусть'],
'❤️': ['красное сердце', 'любовь'],
'🧡': ['оранжевое сердце'],
'💛': ['желтое сердце'],
'💚': ['зеленое сердце'],
'💙': ['синее сердце'],
'💜': ['фиолетовое сердце'],
'🤎': ['коричневое сердце'],
'🖤': ['черное сердце'],
'🤍': ['белое сердце'],
// Животные
'🐶': ['собака', 'пес', 'щенок'],
'🐱': ['кот', 'кошка', 'котенок'],
'🐭': ['мышь', 'мышка'],
'🐹': ['хомяк'],
'🐰': ['кролик', 'зайчик'],
'🦊': ['лиса', 'лисица'],
'🐻': ['медведь', 'мишка'],
'🐼': ['панда'],
'🐨': ['коала'],
'🐯': ['тигр'],
'🦁': ['лев'],
'🐮': ['корова'],
'🐷': ['свинья', 'хрюшка'],
'🐸': ['лягушка'],
'🐵': ['обезьяна'],
'🐔': ['курица'],
'🐧': ['пингвин'],
'🐦': ['птица'],
'🐤': ['цыпленок'],
'🦆': ['утка'],
'🦅': ['орел'],
'🦉': ['сова'],
'🦇': ['летучая мышь'],
'🐺': ['волк'],
'🐗': ['кабан'],
'🐴': ['лошадь', 'конь'],
'🦄': ['единорог'],
'🐝': ['пчела'],
'🐛': ['гусеница', 'червяк'],
'🦋': ['бабочка'],
'🐌': ['улитка'],
'🐞': ['божья коровка'],
'🐢': ['черепаха'],
'🐍': ['змея'],
'🦎': ['ящерица'],
'🐙': ['осьминог'],
'🦀': ['краб'],
'🐡': ['рыба', 'фугу'],
'🐠': ['тропическая рыба'],
'🐟': ['рыба'],
'🐬': ['дельфин'],
'🐳': ['кит'],
'🦈': ['акула'],
// Еда
'🍎': ['яблоко'],
'🍊': ['апельсин'],
'🍋': ['лимон'],
'🍌': ['банан'],
'🍉': ['арбуз'],
'🍇': ['виноград'],
'🍓': ['клубника'],
'🍑': ['персик'],
'🍒': ['вишня'],
'🍍': ['ананас'],
'🥝': ['киви'],
'🥑': ['авокадо'],
'🍅': ['помидор', 'томат'],
'🍆': ['баклажан'],
'🥒': ['огурец'],
'🥕': ['морковь'],
'🌽': ['кукуруза'],
'🌶️': ['перец', 'острый'],
'🥔': ['картофель', 'картошка'],
'🍠': ['батат', 'сладкий картофель'],
'🥐': ['круассан'],
'🥖': ['багет', 'хлеб'],
'🥨': ['крендель'],
'🥞': ['блины'],
'🧀': ['сыр'],
'🍖': ['мясо', 'кость'],
'🍗': ['курица', 'ножка'],
'🥩': ['стейк', 'мясо'],
'🥓': ['бекон'],
'🍔': ['бургер', 'гамбургер'],
'🍟': ['картофель фри', 'фри'],
'🍕': ['пицца'],
'🌭': ['хот-дог'],
'🥪': ['сэндвич', 'бутерброд'],
'🌮': ['тако'],
'🌯': ['буррито'],
'🥙': ['лаваш', 'шаурма'],
'🥚': ['яйцо'],
'🍳': ['яичница'],
'🥘': ['паэлья', 'сковорода'],
'🍲': ['суп', 'рагу'],
'🥣': ['каша', 'миска'],
'🥗': ['салат'],
'🍿': ['попкорн'],
'🧈': ['масло'],
'🧂': ['соль'],
'🥫': ['консервы'],
'🍱': ['бенто', 'ланч'],
'🍘': ['рисовый крекер'],
'🍙': ['онигири'],
'🍚': ['рис'],
'🍛': ['карри'],
'🍜': ['лапша', 'рамен'],
'🍝': ['спагетти', 'паста'],
'🍠': ['батат'],
'🍢': ['оден'],
'🍣': ['суши'],
'🍤': ['креветка жареная'],
'🍥': ['камабоко'],
'🥮': ['лунный пряник'],
'🍡': ['данго'],
'🥟': ['пельмени'],
'🥠': ['печенье с предсказанием'],
'🥡': ['коробка для еды'],
'🦀': ['краб'],
'🦞': ['лобстер'],
'🦐': ['креветка'],
'🦑': ['кальмар'],
'🦪': ['устрица'],
'🍦': ['мороженое'],
'🍧': ['фруктовый лед'],
'🍨': ['мороженое'],
'🍩': ['пончик'],
'🍪': ['печенье'],
'🎂': ['торт', 'день рождения'],
'🍰': ['кусок торта'],
'🧁': ['кекс'],
'🥧': ['пирог'],
'🍫': ['шоколад'],
'🍬': ['конфета'],
'🍭': ['леденец'],
'🍮': ['пудинг'],
'🍯': ['мед'],
'🍼': ['детская бутылочка'],
'🥛': ['молоко'],
'☕': ['кофе', 'чай'],
'🫖': ['чайник'],
'🍵': ['зеленый чай'],
'🍶': ['саке'],
'🍾': ['шампанское'],
'🍷': ['вино'],
'🍸': ['коктейль'],
'🍹': ['тропический напиток'],
'🍺': ['пиво'],
'🍻': ['пиво', 'чирс'],
'🥂': ['бокалы', 'тост'],
'🥃': ['виски', 'стакан'],
'🥤': ['напиток', 'стакан'],
'🧃': ['сок', 'коробка'],
'🧉': ['мате'],
'🧊': ['лед'],
// Праздники и объекты
'🎉': ['праздник', 'вечеринка', 'конфетти'],
'🎊': ['конфетти', 'праздник'],
'🎈': ['шарик', 'воздушный шар'],
'🎁': ['подарок', 'сюрприз'],
'🎀': ['бант', 'лента'],
'🎂': ['торт', 'день рождения'],
'🎄': ['елка', 'новый год', 'рождество'],
'🎅': ['санта', 'дед мороз'],
'🎃': ['тыква', 'хэллоуин'],
'👻': ['привидение', 'призрак'],
'🎆': ['фейерверк', 'салют'],
'🎇': ['бенгальский огонь'],
'✨': ['искры', 'блеск', 'магия'],
'🎈': ['шарик'],
'🎉': ['конфетти', 'праздник'],
// Символы
'❤️': ['сердце', 'любовь'],
'💯': ['сто', 'процентов', 'отлично'],
'💢': ['злость', 'гнев'],
'💥': ['взрыв', 'бум'],
'💫': ['головокружение'],
'💦': ['капли', 'пот'],
'💨': ['ветер', 'быстро'],
'🔥': ['огонь', 'пламя', 'жар'],
'⭐': ['звезда'],
'🌟': ['сияющая звезда'],
'✨': ['искры', 'блеск'],
'⚡': ['молния', 'электричество'],
'☀️': ['солнце'],
'🌙': ['луна', 'месяц'],
'☁️': ['облако'],
'⛅': ['солнце за облаком'],
'⛈️': ['гроза'],
'🌧️': ['дождь'],
'⛄': ['снеговик'],
'❄️': ['снежинка', 'снег'],
'🌈': ['радуга'],
'☂️': ['зонт'],
// Флаги
'🏁': ['финиш', 'флаг'],
'🚩': ['красный флаг'],
'🏴': ['черный флаг'],
'🏳️': ['белый флаг'],
'🏳️‍🌈': ['радужный флаг', 'прайд', 'лгбт'],
'🇷🇺': ['россия', 'российский флаг'],
};
/**
* Функция для поиска эмодзи по русскому запросу
*/
export function searchEmojiRussian(query) {
if (!query) return [];
const lowerQuery = query.toLowerCase().trim();
const results = [];
for (const [emoji, keywords] of Object.entries(emojiRussianKeywords)) {
if (keywords.some(keyword => keyword.includes(lowerQuery))) {
results.push(emoji);
}
}
return results;
}

View File

@ -150,10 +150,18 @@ main.main {
color: var(--bs-body-color);
}
.chat-composer .btn-emoji {
color: var(--bs-body-color);
}
.chat-composer .btn-send {
box-shadow: 0 4px 16px rgba(var(--bs-primary-rgb), 0.35);
}
.chat-composer .message-emoji .dropdown-menu {
background: var(--bs-body-bg);
}
/* Превью вложений */
.attachment-preview {
padding: var(--space-1) 0;
@ -331,10 +339,51 @@ main.main {
bottom: 4.25rem;
z-index: 2;
display: none;
width: 44px;
height: 44px;
border-radius: 50% !important;
padding: 0 !important;
border: none !important;
background: var(--bs-primary) !important;
color: #fff !important;
box-shadow: 0 4px 16px rgba(var(--bs-primary-rgb), 0.25) !important;
transition: transform 0.2s ease, box-shadow 0.2s ease;
display: inline-flex;
align-items: center;
justify-content: center;
opacity: 0;
pointer-events: none;
i {
font-size: 1.25rem;
}
&:hover {
transform: scale(1.08);
box-shadow: 0 6px 20px rgba(var(--bs-primary-rgb), 0.35) !important;
}
&:active {
transform: scale(0.95);
}
}
.scroll-to-bottom.show {
display: block;
display: inline-flex;
opacity: 1;
pointer-events: auto;
animation: fadeInUp 0.3s ease;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* ─────────────────────────────────────────────────────────────

View File

@ -3,12 +3,12 @@
* Унифицированные стили для полей ввода сообщений и комментариев
*/
$message-bg-default: color-mix(in srgb, var(--bs-body-bg) 95%, var(--bs-secondary-bg) 5%);
$message-bg-focused: color-mix(in srgb, var(--bs-body-bg) 85%, var(--bs-secondary-bg) 15%);
$message-bg-default: transparent;
$message-bg-focused: transparent;
.message-field {
display: flex;
align-items: flex-end;
align-items: center;
gap: var(--space-1);
width: 100%;
padding: 0.35rem 0.4rem;
@ -16,6 +16,7 @@ $message-bg-focused: color-mix(in srgb, var(--bs-body-bg) 85%, var(--bs-secondar
border-radius: 999px;
background: $message-bg-default;
transition: border-color 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
--message-action-size: 42px;
}
.message-field:focus-within {
@ -35,6 +36,7 @@ $message-bg-focused: color-mix(in srgb, var(--bs-body-bg) 85%, var(--bs-secondar
.message-field--compact {
padding: 0.25rem 0.3rem;
border-radius: 24px;
--message-action-size: 36px;
}
.message-input,
@ -42,14 +44,16 @@ $message-bg-focused: color-mix(in srgb, var(--bs-body-bg) 85%, var(--bs-secondar
flex: 1 1 auto;
min-height: 44px;
max-height: 260px;
border: none;
background: transparent;
border: none !important;
background: transparent !important;
resize: none;
padding: 0.5rem 0.75rem;
line-height: 1.4;
font-size: 1rem;
color: inherit;
scrollbar-width: none;
box-shadow: none !important;
outline: none !important;
}
.message-field--compact .message-input {
@ -70,32 +74,36 @@ $message-bg-focused: color-mix(in srgb, var(--bs-body-bg) 85%, var(--bs-secondar
.message-input::-webkit-scrollbar {
display: none;
}
.message-icon-btn,
.btn-attachment,
.btn-send,
.message-send {
width: 42px;
height: 42px;
border-radius: 999px;
display: inline-flex;
.message-send,
.btn-emoji {
width: var(--message-action-size, 42px) !important;
min-width: var(--message-action-size, 42px) !important;
height: var(--message-action-size, 42px) !important;
border-radius: 50% !important;
display: inline-flex !important;
align-items: center;
justify-content: center;
padding: 0;
border: none;
padding: 0 !important;
border: none !important;
background: transparent;
color: var(--bs-secondary-color);
transition: background-color 0.15s ease, color 0.15s ease, transform 0.15s ease;
}
.message-field--compact .message-icon-btn,
.message-field--compact .message-send {
width: 36px;
height: 36px;
aspect-ratio: 1 / 1;
flex: 0 0 auto;
line-height: 1;
font-size: 1.05rem;
--bs-btn-padding-x: 0 !important;
--bs-btn-padding-y: 0 !important;
--bs-btn-border-radius: 50% !important;
box-sizing: border-box;
}
.message-icon-btn:hover,
.btn-attachment:hover {
.btn-attachment:hover,
.btn-emoji:hover {
background: var(--bs-secondary-bg);
color: var(--bs-body-color);
transform: scale(1.02);
@ -130,6 +138,38 @@ $message-bg-focused: color-mix(in srgb, var(--bs-body-bg) 85%, var(--bs-secondar
padding: 0.5rem;
}
.message-emoji .dropdown-menu {
min-width: 260px;
padding: 0;
border-radius: 16px;
overflow: hidden;
}
.chat-emoji-picker {
width: min(340px, 90vw);
max-width: 360px;
height: 340px;
border: none;
outline: none;
--background-color: var(--bs-body-bg);
--text-color: var(--bs-body-color);
--category-font-color: var(--bs-secondary-color);
--hover-color: var(--bs-secondary-bg);
--button-active-background: var(--bs-primary-bg-subtle, var(--bs-primary));
--button-hover-background: var(--bs-secondary-bg);
--focus-indicator: var(--bs-primary);
--border-color: color-mix(in srgb, var(--bs-border-color), transparent 25%);
--input-border-color: color-mix(in srgb, var(--bs-border-color), transparent 30%);
--input-placeholder-color: var(--bs-secondary-color);
--input-focus-border-color: var(--bs-primary);
--indicator-color: var(--bs-primary);
// Скрываем кнопку выбора тона кожи
button.favorites {
display: none !important;
}
}
.message-field--solo {
display: block;
border-radius: 24px;
@ -148,11 +188,10 @@ $message-bg-focused: color-mix(in srgb, var(--bs-body-bg) 85%, var(--bs-secondar
border-radius: 28px;
}
.message-send,
.btn-send,
.message-icon-btn,
.btn-attachment {
width: 38px;
height: 38px;
.message-field {
--message-action-size: 38px;
}
.message-field--compact {
--message-action-size: 32px;
}
}

View File

@ -42,8 +42,8 @@
<article class="feed-card chat-box">
<!-- Кнопка «вниз» -->
<button id="scrollBtn" class="btn btn-light shadow scroll-to-bottom" type="button" aria-label="Прокрутить вниз">
<i class="bi-arrow-down-circle"></i>
<button id="scrollBtn" class="scroll-to-bottom" type="button" aria-label="Прокрутить вниз">
<i class="bi-arrow-down"></i>
</button>
<!-- Лента сообщений -->
@ -175,7 +175,7 @@
<div class="message-field">
<!-- Кнопка вложений с выпадающим меню -->
<div class="dropdown message-attach">
<button type="button" class="btn btn-ghost btn-attachment"
<button type="button" class="btn btn-ghost btn-attachment message-icon-btn"
id="attachmentDropdown"
data-bs-toggle="dropdown"
aria-expanded="false"
@ -210,6 +210,21 @@
</ul>
</div>
<!-- Кнопка выбора эмодзи -->
<div class="dropdown message-emoji">
<button type="button" class="btn btn-ghost btn-emoji message-icon-btn"
id="emojiDropdown"
data-bs-toggle="dropdown"
data-emoji-toggle="1"
aria-expanded="false"
title="Вставить смайлик">
<i class="bi-emoji-smile"></i>
</button>
<div class="dropdown-menu dropdown-menu-start message-emoji-menu" aria-labelledby="emojiDropdown">
<emoji-picker data-emoji-picker class="chat-emoji-picker"></emoji-picker>
</div>
</div>
<!-- Поле ввода текста -->
<textarea name="content"
id="id_content"
@ -243,6 +258,7 @@
{% block extra_js %}
{{ block.super }}
<script type="module" src="https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/index.js"></script>
<script type="module" src="{% static 'js/components/chatComposer.js' %}"></script>
<script type="module" src="{% static 'js/components/chatHistoryLoader.js' %}"></script>
{% endblock %}