Чат-виджет: подключение, кастомизация и управление
Руководство по подключению чат-виджета и настройке его внешнего вида и поведения. Описаны атрибуты скрипта (data-launcher, data-greeting, data-open), изоляция стилей через Shadow DOM, CSS-переменные и селекторы ::part(), а также JavaScript API window.uHamkor — методы, снимок состояния, события и практические рецепты. Поддерживаются доступность, печать и reduced motion.
🎨 Чат-виджет: подключение, кастомизация и управление
Виджет подключается одним тегом <script>, изолирует стили через Shadow DOM и управляется с помощью CSS-переменных, селекторов ::part() и JavaScript API window.uHamkor.
🚀 Подключение
<script
src="https://your-widget-host.com/widget.js"
data-project-uuid="YOUR_PROJECT_UUID"
data-language="uz"
defer
></script>
Разместите тег внутри <head> страницы. Виджет инициализируется только на доменах, указанных в списке domains проекта.
⚙️ Атрибуты скрипта
data-project-uuid— обязательный. Проект, которому принадлежит виджет.data-language—uz/ru/en. По умолчанию:uz.data-user— внешний ID пользователя (идентифицирует посетителя).data-launcher—true/false. По умолчаниюtrue. Отрисовывает плавающую кнопку.data-greeting—true/false. По умолчаниюtrue. Приветственное облачко.data-open—true/false. По умолчаниюfalse. Открывает окно чата сразу после монтирования.
data-language, data-user, data-launcher и data-greeting отслеживаются в рантайме — при изменении атрибута виджет обновляется сразу.
🎯 CSS-переменные
Переопределяйте переменные на классе .chat-widget:
.chat-widget {
/* Цвета (HSL без hsl()) */
--cw-primary: 221 83% 53%;
--cw-secondary: 258 83% 66%;
--cw-background: 0 0% 100%;
--cw-foreground: 222.2 84% 4.9%;
/* Расположение */
--cw-z-index: 50;
--cw-button-bottom: 20px;
--cw-button-right: 20px;
--cw-window-bottom: 20px;
--cw-window-right: 20px;
/* Радиус скругления */
--cw-radius: 1rem;
}
Высота окна чата вычисляется из --cw-window-bottom, поэтому при поднятии виджета окно остаётся в пределах экрана.
🧩 Селекторы ::part()
Стилизация внутренних элементов:
.chat-widget::part(button) {}
.chat-widget::part(window) {}
.chat-widget::part(header) {}
.chat-widget::part(body) {}
.chat-widget::part(footer) {}
🖨️ Печать
При печати виджет скрывается автоматически. Дополнительная настройка не требуется.
🎞️ Reduced Motion
Для пользователей с prefers-reduced-motion: reduce анимации отключаются автоматически.
💡 Примеры
📍 Изменить положение виджета
.chat-widget {
--cw-button-bottom: 100px;
--cw-button-right: 30px;
--cw-window-bottom: 100px;
--cw-window-right: 30px;
}
🔝 Более высокий z-index (поверх модальных окон)
.chat-widget {
--cw-z-index: 99999;
}
🚫 Скрыть виджет на определённых страницах
.no-chat .chat-widget {
display: none;
}
🕹️ JavaScript API (window.uHamkor)
Сразу после монтирования виджет публикует глобальный API в window.uHamkor. Через него виджетом можно управлять из вашего интерфейса: кнопка в шапке, ссылка «Поддержка», бейдж непрочитанных сообщений.
📦 Подключение с очередью команд
Вызовы, сделанные до загрузки widget.js, попадают в очередь и выполняются после загрузки — ждать через setTimeout не нужно:
<script>
window.uHamkor = window.uHamkor || function () {
(window.uHamkor.q = window.uHamkor.q || []).push(arguments);
};
uHamkor('hideLauncher'); // сайт рисует свою кнопку
</script>
<script
src="https://your-widget-host.com/widget.js"
data-project-uuid="YOUR_PROJECT_UUID"
data-language="uz"
data-launcher="false"
defer
></script>
После загрузки работают оба стиля вызова:
uHamkor.open(); // форма метода
uHamkor('open'); // форма команды (её же использует очередь)
🛠️ Методы
open()/close()/toggle()— управление окном чата.isOpen()— состояние окна (boolean).showLauncher()/hideLauncher()— показать/скрыть плавающую кнопку. Пока чат открыт, кнопка остаётся видимой и работает как кнопка закрытия.setGreetingEnabled(enabled)— включить/выключить приветственное облачко.setLanguage(lang)—en,ru,uz,uz_latn,uz_cyrl.getLanguage()— текущий язык.identify(identity)—{ externalUserId, firstName, lastName, phoneNumber }.logout()— очищает сессию, токен и кешированные сообщения.prefill(text)— подставляет текст в поле ввода без отправки.sendMessage(text)— отправляет сообщение; возвращаетPromise<boolean>(false— отправить не удалось).getUnreadCount()— количество непрочитанных сообщений.getState()— полный снимок состояния.subscribe(listener)— срабатывает на каждое изменение состояния; возвращает функцию отписки.on(event, handler)/once/off— подписка на события.setPosition({ bottom, right })— перемещает кнопку и окно (в пикселях).setColors(primary, secondary?)— меняет цвета темы в рантайме.destroy()— размонтирует виджет и удаляет его со страницы.
📊 Снимок состояния
{
projectUuid: string;
isOpen: boolean;
isLauncherVisible: boolean;
isWidgetEnabled: boolean; // виджет выключен в проекте
unreadCount: number;
language: string;
isAuthorized: boolean;
externalUserId: string | null;
isTyping: boolean; // оператор/бот печатает
isOnline: boolean; // поддержка онлайн
isBotActive: boolean; // в диалоге нет живого оператора
isAIThinking: boolean;
isResolved: boolean;
callStatus: 'idle' | 'calling' | 'incoming' | 'active' | 'ended' | 'failed';
callType: 'audio' | 'video';
}
📡 События
ready— виджет смонтирован; payload: снимок состояния.state— изменилось любое поле снимка.open/close— без payload.unread—{ count }.message—{ direction: 'incoming' | 'outgoing', message }.operator:joined— оператор подключился к диалогу.chat:resolved— диалог завершён.call:incoming/call:started/call:ended—{ type }.language:change—{ language }.identify—{ externalUserId }.destroy— виджет удалён.
Каждое событие также отправляется на document под именем uhamkor:<event>, поэтому фреймворки могут слушать его без обращения к глобальной переменной:
document.addEventListener('uhamkor:unread', e => {
badge.textContent = e.detail.count;
});
🍳 Рецепты
🔔 Своя кнопка с бейджем непрочитанных
uHamkor('hideLauncher');
uHamkor('on', 'state', state => {
badge.hidden = state.unreadCount === 0;
badge.textContent = state.unreadCount;
trigger.setAttribute('aria-expanded', String(state.isOpen));
});
trigger.addEventListener('click', () => uHamkor.toggle());
👤 Привязать авторизованного пользователя
uHamkor.identify({
externalUserId: user.id,
firstName: user.firstName,
lastName: user.lastName,
phoneNumber: user.phone,
});
💬 Начать диалог по действию на странице
uHamkor.open();
uHamkor.sendMessage('Нужна помощь по заказу #1234');
🚪 Очистить сессию при выходе
uHamkor.logout();
🔄 Поведение виджета
- Плавающую кнопку можно перетаскивать. При открытии чата она возвращается в стандартный угол, а перетаскивание отключается — так кнопка остаётся выровненной с окном.
- Пока чат открыт, кнопка не скрывается: она выполняет роль кнопки закрытия.
- Приветственное облачко скрывается при открытом окне чата.
- Анимации открытия/закрытия идут поэтапно (шапка → сообщения → поле ввода) и отключаются при
prefers-reduced-motion. - Первое приветственное сообщение помечается прочитанным, поэтому
unreadCountрастёт только на реальных новых сообщениях. setPosition({ bottom })перемещает окно вместе с кнопкой.
♿ Доступность
role="dialog"иaria-modal="true"у окна чата.aria-labelу всех интерактивных кнопок.aria-haspopup="dialog"у плавающей кнопки.- Поддержка навигации с клавиатуры.