FKB Pro - قالب اجتماعي

مرحباً @Don ، قمت بإجراء تعديل شخصي على قالب FKB pro لإضافة زر محادثة عائم في الزاوية السفلية اليمنى للأجهزة اللوحية والحاسوب، وفي الزاوية السفلية اليسرى للهاتف المحمول، مع تأثير مشابه على زر إنشاء الموضوع.

إليك كود المكوّن الخاص بي: css

/* ========================================================= */
/* 1. الزر العائم الرئيسي (نمط Messenger / Discord) */
/* ========================================================= */
.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 });
});