FKB Pro - тема для соцсетей

Единственная маленькая проблема с кодом scss: нет автоматического переноса строки, поэтому слова обрезаются :wink:

Что вы имеете в виду? Можете отправить скриншот?

Вот скриншот с моего телефона. Обратите внимание, что название категории не отображается полностью.

1 лайк

Это намеренное поведение в ядре, но если вы предпочитаете, чтобы название категории переносилось на новую строку, а не обрезалось, попробуйте следующее:

.category-heading {
  &.--has-logo {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: var(--space-4);
  }
  &__content {
    min-width: 0;
    .badge-category__wrapper {
      max-width: 100%;
      white-space: normal;
      overflow-wrap: anywhere;
      .badge-category {
        &.--style-emoji,
        &.--style-icon {
          align-items: baseline;
        }
      }
    }
  }
}
3 лайка

Супер, работает! @Don, ты просто мастер :clap:

1 лайк

Я заметил, что в планшетной версии нет описания категории, а в мобильной — есть. Думаю, было бы разумно сделать то же самое и в мобильной версии. :thinking:

Я тоже заметил, что при использовании вложенных ответов есть небольшой дефект отображения аватара — он немного большой.

Не знаю, только ли я с этим сталкиваюсь?

Спасибо, @gilles! Я объединил обновление для этих…

2 лайка

Я протестировал, всё работает, спасибо @Don за оперативность :clap: :+1:

1 лайк

@Don я нашёл небольшой баг отображения в режиме «Десктоп» на компьютере без боковой панели, к сведению :wink:

1 лайк

Спасибо, я исправил это здесь: FIX: Enhance category heading styles in SCSS · VaperinaDEV/fkb-pro-theme@95a3c25 · GitHub

4 лайка

Протестировано и одобрено :+1:

1 лайк

Я только что это установил. Мне кажется, что сразу после установки справа была панель Fakebook, но у неё она исчезла. Не уверен, что я изменил, чтобы она пропала, и как её вернуть.

Привет :waving_hand: В правом нижнем углу есть кнопка, чтобы переключить этот раздел.

Я обновил тему: DEV: Improve FKB panel and right sidebar layout handling - Pull Request #78 - VaperinaDEV/fkb-pro-theme - GitHub

2 лайка

А, вот оно где — спасибо! Хм, наверное, я случайно нажал? Или это выключилось, когда я открыл это на мобильном? Я проведу несколько экспериментов и отпишусь.

1 лайк

Да, на мобильных устройствах он не отображается.

Да, конечно.

Я думал, что возможно, его отключили для меня на десктопе, когда я читал наш форум с мобильного. Я только что провёл этот тест, и панель не была отключена. Так что остаётся загадкой, как же моя панель была отключена на десктопе — видимо, я случайно нажал на стрелку отключения.

1 лайк

Привет, Дон! Мне очень нравится тема FKB Pro.

Небольшая просьба: можно ли добавить настройку, чтобы на карточках тем отображались аватары нескольких участников, а не только последнего автора?

Один аватар выглядит очень аккуратно, но отображение нескольких участников действительно подчёркивает, насколько активным является обсуждение. Переключатель для этой функции был бы просто замечательным!

Спасибо за твою отличную работу! Лео

Привет, @Don! Я внёс небольшое личное изменение в тему FKB pro, чтобы добавить плавающую кнопку чата в правом нижнем углу для планшетов и десктопов, а на мобильных устройствах — в левом нижнем углу, с тем же эффектом, что и у кнопки создания темы. Думаю, в коде можно сделать лучше, но это работает :rofl:

Вот мой код компонента: css


// 1. ГЛАВНАЯ ПЛАВАЮЩАЯ КНОПКА 
.header-dropdown-toggle.chat-header-icon {
    position: fixed !important;
    bottom: 60px !important;
    right: 20px !important;
    z-index: 9999 !important;
    
    /* Идеальные размеры (круг) */
    width: 60px !important;
    height: 60px !important;
    min-width: 60px !important;
    min-height: 60px !important;
    border-radius: 50% !important;
    
    /* Яркий синий цвет и цветная тень */
    background: #0084ff !important;
    box-shadow: 0 4px 14px rgba(0, 132, 255, 0.4), 0 2px 4px rgba(0, 0, 0, 0.1) !important;
    
    /* Устранение отступов */
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    padding: 0 !important;
    margin: 0 !important;
    border: none !important;
    
    /* Плавный переход */
    transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.3s ease, box-shadow 0.2s ease !important;
}

/* Специальное позиционирование на мобильных (экраны <= 640px) */
@media (max-width: 640px) {
    .header-dropdown-toggle.chat-header-icon {
        bottom: 80px !important; 
        left: 20px !important;   /* Измените на right: 20px, если хотите вернуть кнопку вправо */
    }
}

/* Эффект при наведении (небольшое увеличение) */
.header-dropdown-toggle.chat-header-icon:hover {
    transform: scale(1.08) !important;
    box-shadow: 0 6px 20px rgba(0, 132, 255, 0.5), 0 4px 8px rgba(0, 0, 0, 0.15) !important;
}


/* 2. УСТРАНЕНИЕ СЕРЫЙ ФОНА ПО УМОЛЧАНИЮ DISCOURSE                 */

.header-dropdown-toggle.chat-header-icon .icon,
.header-dropdown-toggle.chat-header-icon span:not(.badge-notification):not(.chat-badge):not([class*="unread"]):not([class*="count"]) {
    background-color: transparent !important;
    box-shadow: none !important;
    border: none !important;
}


/* 3. ИДЕАЛЬНОЕ ЦЕНТРИРОВАНИЕ ИКОНКИ (пузырька)                  */

.header-dropdown-toggle.chat-header-icon .d-icon,
.header-dropdown-toggle.chat-header-icon svg {
    position: absolute !important;
    top: 50% !important;
    left: 50% !important;
    transform: translate(-50%, -50%) !important; 
    
    margin: 0 !important;
    padding: 0 !important;
    width: 32px !important;
    height: 32px !important;
    color: #ffffff !important;
    
    filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 0.1)) !important;
}


/* 4. БЭДЖ УВЕДОМЛЕНИЙ (зеленый счетчик + видимое число) */

.header-dropdown-toggle.chat-header-icon .badge-notification,
.header-dropdown-toggle.chat-header-icon .chat-badge,
.header-dropdown-toggle.chat-header-icon .unread-notifications,
.header-dropdown-toggle.chat-header-icon [class*="badge"],
.d-header-icons .header-dropdown-toggle.chat-header-icon .badge-notification {
    position: absolute !important;
    top: -4px !important;
    right: -4px !important;
    left: auto !important;
    margin: 0 !important;
    z-index: 10 !important; 
    
    /* Принудительный зеленый цвет */
    background-color: #53A548 !important;  
    background: #53A548 !important;
    color: #ffffff !important;
    
    /* Принудительная типографика для видимости числа */
    font-size: 13px !important;
    font-weight: bold !important;
    line-height: 18px !important;
    letter-spacing: normal !important;
    text-indent: 0 !important;
    
    /* Размеры и форма */
    padding: 0 6px !important;
    min-width: 22px !important;
    height: 22px !important;
    border-radius: 50px !important;
    border: 2px solid #ffffff !important;
    box-sizing: border-box !important;
    
    /* Центрирование числа */
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    white-space: nowrap !important;
    overflow: visible !important;
    
    /* Зеленая пульсация (связана с вашими keyframes) */
    animation: badge-pulse 2s infinite !important; 
}

/* Принудительное отображение всех дочерних элементов (если Discourse оборачивает число в span) */
.header-dropdown-toggle.chat-header-icon .badge-notification *,
.header-dropdown-toggle.chat-header-icon .chat-badge *,
.header-dropdown-toggle.chat-header-icon [class*="badge"] * {
    color: #ffffff !important;
    background: transparent !important;
    display: inline-block !important;
    visibility: visible !important;
    opacity: 1 !important;
    font-size: inherit !important;
}

/* 5. ИСПРАВЛЕННЫЕ АНИМАЦИИ (эффект радара и отскок)            */


/* 5.1 - Радарная волна (пульсация) вокруг красного бэджа */
.header-dropdown-toggle.chat-header-icon .badge-notification,
.header-dropdown-toggle.chat-header-icon .chat-badge,
.header-dropdown-toggle.chat-header-icon .chat-channel-unread-indicator {
    animation: badge-pulse 2s infinite !important;
    transform-origin: center !important;
}

/* 5.2 - Эффект отскока всей синей кнопки при наличии сообщения */
.header-dropdown-toggle.chat-header-icon:has(.badge-notification),
.header-dropdown-toggle.chat-header-icon:has(.chat-badge),
.header-dropdown-toggle.chat-header-icon:has(.chat-channel-unread-indicator) {
    animation: chat-bounce 3s infinite !important;
}

/* --- ОПРЕДЕЛЕНИЕ KEYFRAMES (не изменять) --- */

/* Определение волны: используем только box-shadow, чтобы избежать ошибок отображения */
@keyframes badge-pulse {
    0% {
        box-shadow: 0 0 0 0 rgba(250, 62, 62, 0.8);
    }
    70% {
        box-shadow: 0 0 0 10px rgba(250, 62, 62, 0);
    }
    100% {
        box-shadow: 0 0 0 0 rgba(250, 62, 62, 0);
    }
}

/* Определение отскока: кнопка слегка подпрыгивает каждые 3 секунды */
@keyframes chat-bounce {
    0%, 20%, 50%, 80%, 100% { 
        transform: translateY(0); 
    }
    40% { 
        transform: translateY(-6px); 
    }
    60% { 
        transform: translateY(-3px); 
    }
}

/* 6. ПУЛЬСАЦИЯ В СПИСКЕ КАНАЛОВ И ЛИЧНЫХ СООБЩЕНИЙ  */


/* 6.1 - Разрешаем переполнение в контейнерах списка */
.chat-channel-row,
.chat-channel-list-item,
.chat-drawer-container .chat-channel-unread-indicator,
.chat-channel-row .chat-channel-unread-indicator {
    overflow: visible !important;
}

/* 6.2 - Стиль и отступы индикатора-точки */
.chat-channel-row .chat-channel-unread-indicator,
.chat-channel-row .badge-notification,
.chat-channel-row .unread-indicator,
.chat-drawer-container .chat-channel-unread-indicator,
.chat-full-page .chat-channel-unread-indicator,
.chat-channel-list-item .chat-channel-unread-indicator {
    animation: list-dot-pulse 1.8s infinite !important;
    border-radius: 50% !important;
    position: relative !important;
    overflow: visible !important;
    margin: 0 6px !important; /* Отступ, чтобы не прилипать к тексту и дате */
}

/* 6.3 - Откорректированная анимация (более компактная волна, макс. 6px) */
@keyframes list-dot-pulse {
    0% {
        box-shadow: 0 0 0 0 rgba(0, 132, 255, 0.7); /* Адаптируется к цвету точки */
    }
    70% {
        box-shadow: 0 0 0 6px rgba(0, 132, 255, 0); /* Уменьшенный радиус, чтобы оставаться в пределах строки */
    }
    100% {
        box-shadow: 0 0 0 0 rgba(0, 132, 255, 0);
    }
}

и JS:

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer((api) => {
    let lastScrollTop = 0;
    let isHiddenByScroll = false;

    // 1. Обработчик прокрутки (активен ТОЛЬКО на мобильных)
    window.addEventListener("scroll", () => {
        const isMobile = window.innerWidth <= 640;
        const floatBtn = document.querySelector('.header-dropdown-toggle.chat-header-icon');

        if (!floatBtn) return;

        // Если мы на компьютере, убеждаемся, что кнопка всегда видна
        if (!isMobile) {
            if (isHiddenByScroll) {
                floatBtn.style.setProperty('transform', 'translateY(0) scale(1)', 'important');
                floatBtn.style.setProperty('opacity', '1', 'important');
                isHiddenByScroll = false;
            }
            return;
        }

        const currentScrollTop = window.pageYOffset || document.documentElement.scrollTop;

        // Если прокрутка вниз (и мы прошли 100px)
        if (currentScrollTop > lastScrollTop && currentScrollTop > 100) {
            if (!isHiddenByScroll) {
                floatBtn.style.setProperty('transform', 'translateY(100px) scale(0.8)', 'important');
                floatBtn.style.setProperty('opacity', '0', 'important');
                isHiddenByScroll = true;
            }
        } 
        // Если прокрутка вверх
        else if (currentScrollTop < lastScrollTop) {
            if (isHiddenByScroll) {
                floatBtn.style.setProperty('transform', 'translateY(0) scale(1)', 'important');
                floatBtn.style.setProperty('opacity', '1', 'important');
                isHiddenByScroll = false;
            }
        }
        lastScrollTop = currentScrollTop <= 0 ? 0 : currentScrollTop;
    }, { passive: true });

    // 2. MutationObserver для управления базовым отображением
    const observer = new MutationObserver(() => {
        const chatOpen = document.querySelector('.chat-drawer, .d-chat-floating-panel, .chat-widget, .chat-panel, .chat-out-of-container, .chat-full-page');
        const isChatRoute = window.location.pathname.startsWith('/chat');
        const hasFullPageClass = document.body.classList.contains('has-full-page-chat');
        
        const isMobile = window.innerWidth <= 640;
        const isTopicRoute = window.location.pathname.includes('/t/') || document.body.classList.contains('archetype-regular');
        const hideOnMobileTopic = isMobile && isTopicRoute;

        const floatBtn = document.querySelector('.header-dropdown-toggle.chat-header-icon');
        
        if (floatBtn) {
            if (chatOpen || isChatRoute || hasFullPageClass || hideOnMobileTopic) {
                floatBtn.style.setProperty('display', 'none', 'important');
            } else {
                floatBtn.style.setProperty('display', 'flex', 'important');
                
                // Корректное управление позицией для мобильных / десктопа
                if (isMobile) {
                    floatBtn.style.setProperty('left', '10px', 'important'); // Измените значение, если хотите сдвинуть дальше
                    floatBtn.style.removeProperty('right');
                } else {
                    floatBtn.style.setProperty('right', '20px', 'important');
                    floatBtn.style.removeProperty('left');
                }
            }
        }
    });

    observer.observe(document.body, { childList: true, subtree: true, attributes: true });
});