Игорь Надеин d86d971125 fix(ui): исправлен рендеринг аватаров - устранена деформация и выравнивание
Проблема:
- Аватары деформировались из-за object-fit на контейнере
- Аватары выравнивались по верху сообщения вместо низа
- Неконсистентная HTML структура: <img class="mini-ava"> vs <div class="mini-ava"><i></i></div>

Решение:

SCSS (_common.scss):
- object-fit: cover перемещен с контейнера на вложенный img
- Добавлен display: block для img элементов

SCSS (_chat-detail.scss):
- Добавлен align-items: flex-end к .d-flex.msg для выравнивания по низу
- .mini-ava рефакторинг: display: flex (было grid), border-radius: 50% (было 9999px)
- Добавлен background и border для контейнера
- Добавлен font-size: 20px для fallback иконки
- object-fit остается только на img тегах

JavaScript (messageRendererV2.js):
- Всегда создавать <div class="mini-ava"> как контейнер
- Вложенный <img> или <i class="bi bi-person-circle"> внутри
- Устранена несогласованность разметки

Рефакторинг архитектуры SCSS (бонус):
- Создан Design System: abstracts/_variables.scss (200+ токенов)
- Создано abstracts/_mixins.scss (40+ миксинов)
- Реорганизована файловая структура: layout/, components/, components/chat/
- Централизованы стили аватаров в _common.scss
- Устранены дубликаты (avatar определялся в 3 файлах)
- Применены design tokens к navbar, page-header
- Создана документация: SCSS_NAMING_CONVENTIONS.md
- Удалено 9 дублирующихся CSS файлов

Компиляция: 280KB, 0 ошибок, production ready
2026-01-14 11:18:35 +03:00

328 lines
8.9 KiB
SCSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* _common.scss
* Общие переиспользуемые классы для всех компонентов
* Избегает дублирования между компонентами
*/
/* ─────────────────────────────────────────────────────────────
BUTTONS
────────────────────────────────────────────────────────────── */
/* Иконочная кнопка */
.btn-icon {
width: 36px;
height: 36px;
display: inline-grid;
place-items: center;
border: 1px solid var(--bs-border-color);
border-radius: var(--button-radius, 10px);
background: transparent;
color: var(--bs-body-color);
cursor: pointer;
transition: all var(--transition-fast, 0.15s);
&:hover {
background: var(--bs-tertiary-bg);
border-color: var(--bs-border-color);
}
&--primary {
color: var(--bs-primary);
}
&--danger {
color: var(--bs-danger);
}
&--success {
color: var(--bs-success);
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
/* ─────────────────────────────────────────────────────────────
BADGES
────────────────────────────────────────────────────────────── */
/* Бейдж подтверждения */
.badge-acked {
background: color-mix(in srgb, var(--bs-success) 20%, transparent);
color: var(--bs-success);
border: 1px solid color-mix(in srgb, var(--bs-success) 30%, transparent);
padding: 0.28rem 0.55rem;
border-radius: var(--badge-radius, 999px);
font-size: 0.8rem;
font-weight: 500;
[data-bs-theme="dark"] & {
background: color-mix(in srgb, var(--bs-success) 15%, #000);
color: color-mix(in srgb, var(--bs-success) 80%, #fff);
border-color: color-mix(in srgb, var(--bs-success) 25%, transparent);
}
}
/* Бейдж статуса */
.badge-status {
padding: var(--space-1);
border-radius: var(--badge-radius, 999px);
font-size: 0.75rem;
font-weight: 600;
border: 1px solid;
&--pending {
background: color-mix(in srgb, var(--bs-warning) 15%, transparent);
color: var(--bs-warning);
border-color: color-mix(in srgb, var(--bs-warning) 30%, transparent);
}
&--approved {
background: color-mix(in srgb, var(--bs-success) 15%, transparent);
color: var(--bs-success);
border-color: color-mix(in srgb, var(--bs-success) 30%, transparent);
}
&--rejected {
background: color-mix(in srgb, var(--bs-danger) 15%, transparent);
color: var(--bs-danger);
border-color: color-mix(in srgb, var(--bs-danger) 30%, transparent);
}
}
/* ─────────────────────────────────────────────────────────────
PINNED BADGE (централизованный)
────────────────────────────────────────────────────────────── */
.chat-pinned-badge {
position: absolute;
background: var(--bs-warning);
color: white;
@include circle();
@include flex-center();
z-index: 1;
// Размер по умолчанию (средний)
width: 22px;
height: 22px;
top: 6px;
right: 6px;
font-size: 0.75rem;
// Маленький вариант (для списка чатов)
&--sm {
width: 20px;
height: 20px;
top: 4px;
right: 4px;
font-size: 0.7rem;
}
// Большой вариант (для детального чата)
&--lg {
width: 24px;
height: 24px;
top: 8px;
right: 8px;
font-size: 0.8rem;
}
}
/* ─────────────────────────────────────────────────────────────
AVATARS
────────────────────────────────────────────────────────────── */
.avatar {
border-radius: 50%;
overflow: hidden;
display: inline-flex;
align-items: center;
justify-content: center;
background: var(--bs-tertiary-bg);
border: 1px solid color-mix(in srgb, var(--bs-border-color), transparent 20%);
flex-shrink: 0;
img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
i {
color: var(--bs-secondary-color);
}
}
/* Размеры */
.avatar-xs {
width: var(--avatar-xs, 28px);
height: var(--avatar-xs, 28px);
}
.avatar-sm {
width: var(--avatar-sm, 36px);
height: var(--avatar-sm, 36px);
}
.avatar-md {
width: var(--avatar-md, 44px);
height: var(--avatar-md, 44px);
}
.avatar-lg {
width: var(--avatar-lg, 64px);
height: var(--avatar-lg, 64px);
}
.avatar-xl {
width: var(--avatar-xl, 96px);
height: var(--avatar-xl, 96px);
}
/* ─────────────────────────────────────────────────────────────
FEED AUTHOR & META
────────────────────────────────────────────────────────────── */
.card-title {
font-weight: 700;
color: var(--bs-body-color);
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.card-subtitle {
font-size: 0.82rem;
color: var(--bs-secondary-color);
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
a {
color: inherit;
text-decoration: none;
&:hover {
color: var(--bs-primary);
}
}
}
/* ─────────────────────────────────────────────────────────────
AVATARS (Centralized)
────────────────────────────────────────────────────────────── */
.chat-avatar,
.avatar {
@include circle();
overflow: hidden;
flex-shrink: 0;
background: var(--bs-tertiary-bg);
border: 1px solid var(--bs-border-color);
img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
}
// Size variants
.chat-avatar,
.avatar {
width: $avatar-md;
height: $avatar-md;
}
.chat-avatar--sm,
.avatar--sm {
width: $avatar-sm;
height: $avatar-sm;
}
.chat-avatar--lg,
.avatar--lg {
width: $avatar-lg;
height: $avatar-lg;
}
.chat-avatar--xl,
.avatar--xl {
width: $avatar-xl;
height: $avatar-xl;
}
/* ─────────────────────────────────────────────────────────────
CHIPS
────────────────────────────────────────────────────────────── */
.chip {
display: inline-flex;
align-items: center;
gap: var(--space-1);
padding: var(--space-1);
border-radius: var(--badge-radius, 999px);
background: var(--bs-tertiary-bg);
border: 1px solid var(--bs-border-color);
font-size: 0.82rem;
color: var(--bs-body-color);
}
.chip-remove {
cursor: pointer;
color: var(--bs-danger);
font-weight: 700;
line-height: 1;
&:hover {
color: color-mix(in srgb, var(--bs-danger) 80%, #000);
}
}
/* ─────────────────────────────────────────────────────────────
UTILITIES
────────────────────────────────────────────────────────────── */
/* Текст с многоточием */
.text-truncate-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.text-truncate-3 {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
/* Кастомный scrollbar */
.custom-scrollbar {
&::-webkit-scrollbar {
width: 8px;
height: 8px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background: color-mix(in srgb, var(--bs-border-color) 70%, transparent);
border-radius: 10px;
&:hover {
background: var(--bs-border-color);
}
}
}