Список пользователей

1
Админ
Постов: 288
2
VIP
Постов: 75
3
Элита
Постов: 62
4
Проверенные
Постов: 46
5
Проверенные
Постов: 45
6
Проверенные
Постов: 39
7
VIP
Постов: 36
8
VIP
Постов: 35

  • Страница 1 из 1
  • 1
Статус онлайн модуль ВК «Друзья» для Ucoz v 6.0
Дата: Четверг, 08.01.2026, 14:04 | Сообщение # 1 | | Написал: Известный
Автор темы
Мурчанн не в сети
        Сообщений:288
         Регистрация:20.10.2016
За уважение окружающих За уважение окружающих За хорошее поведение За преданность форуму За любовь народа

По мере возможностей решил добавлять новые функции во всплывающее окно модуля «Друзья».

Добавлена новая фишка: индикатор онлайн-статуса. Если пользователь онлайн, во внешнем блоке отображается зелёная точка; если офлайн — индикатор не показывается. Во всплывающем окне статус отображается под аватаркой красным или зелёным цветом, в зависимости от состояния. Изначально я планировал использовать только точки, но решил добавить цветовое выделение; возможно, в других версиях вернусь к более простому варианту.



Также рассматриваю возможность реализации функции разделения друзей на онлайн и офлайн, аналогично тому, как это сделано во «ВКонтакте». Там нет всплывающего окна, и создание отдельной страницы для модуля друзей изначально показалось неправильным. Однако возможно, что в будущем я создам полноценную страницу для модуля, чтобы при клике в левом меню открывалась именно она, а не всплывающее окно.

Скрипт был переписан, внесены изменения, а подход и логика работы немного изменены. Исходный код я представляю в развернутом виде — вы сами сможете упаковать его в CSS и JS. В нашем случае это рабочее решение, и когда вы увидите исходный код, станет понятно, как применяются функции.

В скрипте используется скрытый контейнер для хранения списка онлайн-пользователей:

Код
<!-- Скрытый контейнер со списком онлайн-пользователей -->
<div id="uc-online-list" style="display:none;">
    $ONLINE_USERS_LIST$
</div>


Этот контейнер изначально скрыт
Код
(display: none;)
и служит как «хранилище» данных. Скрипт обращается к нему, чтобы получить актуальный список онлайн-пользователей и отобразить их в нужных блоках всплывающего окна или внешнего интерфейса. Такой подход позволяет отделить данные от визуальной части и облегчает работу с динамическим обновлением статусов.

Исходник

Код
<!-- MINI BLOCK ДРУЗЬЯ -->
<div class="vk-old-block">
<div class="vk-old-header">Друзья</div>
<div class="vk-old-footer" id="friends-count">0 друзей</div>
<div class="vk-old-content">
<div class="vk-friends"></div>
</div>
</div>

<style>
/* ================= MINI BLOCK ================= */
.vk-old-block {
max-width:420px;
background:#fff;
border:1px solid #d1d5da;
font-family:Tahoma,Arial,sans-serif;
font-size:12px;
border-radius:16px;
overflow:hidden;
}

.vk-old-header {
background:linear-gradient(180deg,#5d82b9,#4a6fa8);
color:#fff;
font-weight:bold;
font-size:16px;
padding:10px 12px;
text-align:left;
border-bottom:1px solid #d1d5da;
border-top-left-radius:16px;
border-top-right-radius:16px;
}

.vk-old-content {
padding:10px;
}

.vk-old-footer {
background:#f7f7f7;
border-top:1px solid #d1d5da;
padding:6px 12px;
color:#666;
font-size:12px;
cursor:pointer;
text-align:left;
border-bottom-left-radius:0px;
border-bottom-right-radius:0px;
}

/* ================= MINI FRIENDS GRID ================= */
.vk-friends {
display:grid;
grid-template-columns:repeat(3, 1fr);
gap:10px 10px;
justify-items:center;
}

.vk-friend {
text-align:center;
cursor:pointer;
}

.vk-friend img {
width:82px;
height:82px;
border-radius:50%;
object-fit:cover;
}

.vk-friend span {
display:block;
margin-top:4px;
font-size:12px;
color:#1d2c45;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
}

/* При наведении — слегка больше яркости */
.vk-friend:hover {
filter: brightness(1.45); /* затемнение */
transform: scale(1.00); /* лёгкое увеличение */
}

/* ================= OVERLAY ================= */
.vk-ui-overlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.15);
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
}

.vk-ui-popup {
width: 400px; /* чуть уже */
max-height: 190vh; /* максимум по высоте экрана */
height: auto; /* автоматическая высота */
background: #f5f6f8;
border-radius: 24px;
display: flex;
flex-direction: column;
overflow: visible; /* важно */
box-shadow: 0 30px 70px rgba(0,0,0,0.25);
font-family: system-ui, -apple-system, Arial, sans-serif;
border: 1px solid #d1d5da;
padding: 0;
}

/* ================= HEADER ================= */
.vk-ui-header {
padding: 18px 24px;
display: flex;
align-items: center;
justify-content: space-between; /* "Друзья" слева, поиск справа */
background: rgba(93,130,185,0.9); /* стеклянный синий */
backdrop-filter: blur(14px) saturate(180%) brightness(1.05);
-webkit-backdrop-filter: blur(14px) saturate(180%) brightness(1.05);
border-bottom: 1px solid rgba(0,0,0,0.12);
border-top-left-radius: 24px;
border-top-right-radius: 24px;
}

/* Заголовок "Друзья" */
.vk-ui-title {
font-size: 20px;
font-weight: 600;
margin: 0;
color: #ffffff;
}

/* Поиск справа */
.vk-ui-search {
flex-shrink: 0;
}

.vk-ui-search input {
width: 160px;
padding: 8px 14px 8px 36px; /* минимальные отступы */
border-radius: 14px;
border: none;
outline: none;
font-size: 15px;
background: rgba(255,255,255,0.95);
color: #1d2c45;
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' fill='%2390a4c0'%3E%3Cpath d='M17.5 16l-4.3-4.3a7 7 0 1 0-1.4 1.4L16 17.5zM7 12a5 5 0 1 1 0-10 5 5 0 0 1 0 10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: 10px center;
background-size: 18px 18px;
}

/* ================= INNER BLOCK ================= */
.vk-ui-inner {
background: #f9f9f9; /* светло-серый фон для внутреннего блока */
padding: 14px; /* отступы внутри popup */
}

/* Внутренний белый блок */
.vk-ui-inner .vk-ui-subblock {
background: #ffffff; /* сам белый блок внутри */
padding: 12px; /* отступы внутри блока */
border-radius: 12px;
border: 1px solid #e4e8f0;
margin-top: 12px; /* небольшой отступ от верхнего серого фона */
}

/* ================= FRIEND LIST ================= */
.vk-ui-list {
max-height: 62vh; /* больше места */
overflow-y: auto; /* прокрутка внутри */
padding: 12px;
margin: 12px;
background: #fff;
border-radius: 12px;
border: 1px solid #e4e8f0;
box-sizing: border-box;
flex: 1; /* тянется на всю доступную высоту */
}

/* Скрываем скролл для Webkit-браузеров (Chrome, Edge, Safari) */
.vk-ui-list::-webkit-scrollbar {
width: 0;
height: 0;
}

/* ROW */
.vk-ui-row {
display: flex;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid #e4e8f0;
cursor: pointer;
transition: background 0.2s ease;
}

.vk-ui-row:last-child {
border-bottom: none;
}

.vk-ui-row:hover {
background: #f5f7fb;
}

/* =========================
AVATAR + ONLINE STATUS
========================= */
.vk-ui-ava {
position: relative;
width: 88px;
height: 88px;
margin-bottom: 12px; /* расстояние под аватаркой до текста/статуса */
}

/* Аватарка */
.vk-ui-ava img {
width: 88px;
height: 88px;
border-radius: 14px;
object-fit: cover;
box-shadow: 0 4px 14px rgba(0,0,0,0.25);
}

.vk-ui-ava {
margin-bottom: 16px; /* больше пространства под аватаркой */
}

.vk-ui-ava .statusOnline {
margin: 12px auto 0 auto; /* поднимаем статус ниже аватарки */
}

.vk-ui-ava .statusOffline {
margin: 12px auto 0 auto; /* поднимаем статус ниже аватарки */
}

/* STATUS LETTER (кружок с буквой) */
.vk-ui-letter.friend { background: #6b93c7; } /* Друг */
.vk-ui-letter.idol { background: #c84b4b; } /* Кумир */
.vk-ui-letter.family { background: #4a9e4a; } /* Семья */
.vk-ui-letter.acquaintance { background: #8a8a8a; }/* Знакомый */
.vk-ui-letter.buddy { background: #d18c3b; } /* Приятель */
.vk-ui-letter.nostatus { background: #bbbbbb; } /* Без статуса */

/* BADGE (текстовый статус) */
.vk-ui-badge.friend { background: #6b93c7; }
.vk-ui-badge.idol { background: #c84b4b; }
.vk-ui-badge.family { background: #4a9e4a; }
.vk-ui-badge.acquaintance { background: #8a8a8a; }
.vk-ui-badge.buddy { background: #d18c3b; }
.vk-ui-badge.nostatus { background: #bbbbbb; }

/* STATUS LETTER-2 */
.vk-ui-letter {
position: absolute;
bottom: -4px;
left: -4px;
width: 26px;
height: 26px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 14px;
color: #fff;
}

.vk-ui-letter.friend { background: #6b93c7; }
.vk-ui-letter.idol { background: #c84b4b; }

/* INFO */
.vk-ui-info {
flex: 1;
margin-left: 14px;
}

.vk-ui-name {
font-size: 20px;
font-weight: 600;
color: #1d2c45;
}

.vk-ui-badge {
margin-top: 6px;
padding: 6px 14px;
border-radius: 999px;
font-size: 14px;
display: inline-block;
color: #fff;
}

.vk-ui-badge.friend { background: #6b93c7; }
.vk-ui-badge.idol { background: #c84b4b; }
.vk-ui-badge.nostatus { background: #8a8a8a; }

/* ARROW */
.vk-ui-arrow {
font-size: 28px;
color: #b6bfd6;
}

/* Кружок с числом друзей */
.vk-ui-title-count {
display: inline-flex;
align-items: center;
justify-content: center;
width: 27px; /* размер кружка */
height: 27px;
margin-left: 0px; /* отступ от слова "Друзья" */
border-radius: 50%;
background: #fff; /* белый фон */
border: 1px solid #7e7f80; /* белая обводка */
color: #7e7f80; /* серый цвет текста */
font-size: 14px;
font-weight: 700;
}

/* ==============================
Мини-грид — точки на аватарке (только онлайн)
============================== */
.vk-friend {
position: relative; /* точка позиционируется относительно аватарки */
display: inline-block;
}

.vk-friend img {
border-radius: 50%; /* аватарка круглая */
display: block;
}

/* ==== Точка онлайн ==== */
.vk-friend .statusDot {
position: absolute;
width: 16px; /* размер точки */
height: 16px;
border-radius: 50%; /* круглая точка */
border: 2px solid #fff; /* белая обводка */
box-shadow: 0 0 2px rgba(0,0,0,0.5);

bottom: 18px; /* вертикальное положение */
right: 12px; /* смещение вправо */
}

/* Цвет точки только для онлайн */
.vk-friend .onlineDot {
background-color: #3eb749; /* зелёная */
}

/* Убираем точку, если офлайн */
.vk-friend .offlineDot {
display: none;
}

/* ==============================
Всплывающее окно — аватар и статус
============================== */
.vk-ui-ava {
text-align: center;
position: relative;
margin-bottom: 22px; /* отступ под аватаркой */
}

/* ==== Точка статуса во всплывающем окне ==== */
.vk-ui-ava .statusDot {
position: absolute;
width: 14px; /* размер точки */
height: 14px;
border-radius: 50%;
border: 2px solid #fff;
box-shadow: 0 0 2px rgba(0,0,0,0.5);

bottom: 6px; /* регулируем вертикальное положение */
right: 6px; /* смещение вправо */
}

/* Цвет точки по статусу */
.vk-ui-ava .onlineDot { background-color: #3eb749; } /* онлайн зелёная */
.vk-ui-ava .offlineDot { background-color: #ff3b3b; } /* офлайн красная */

/* ==== Статус текстом под аватаркой ==== */
.vk-ui-ava .statusOnline {
display: block;
margin: 6px auto 0 auto; /* вертикальное положение, можно регулировать */
padding: 2px 8px;
font-size: 10px;
font-weight: bold;
text-transform: uppercase;
color: #fff;
background-color: #3eb749;
border-radius: 4px;
text-align: center;
width: fit-content;
}

.vk-ui-ava .statusOffline {
display: block;
margin: 6px auto 0 auto; /* вертикальное положение, можно регулировать */
padding: 2px 8px;
font-size: 10px;
font-weight: bold;
text-transform: uppercase;
color: #fff;
background-color: #ff3b3b;
border-radius: 4px;
text-align: center;
width: fit-content;
}

/* ==============================
Настройки
============================== */
/* Размер точки */
.vk-ui-ava .statusDot { width: 16px; height: 16px; }

/* Смещение точки */
.vk-ui-ava .statusDot { bottom: 10px; right: 10px; }
</style>

<!-- Скрытый контейнер со списком онлайн-пользователей -->
<div id="uc-online-list" style="display:none;">
$ONLINE_USERS_LIST$
</div>

<script>
$(function(){

<?if(!$USER_LOGGED_IN$)?>

$('#friends-count').text('Друзей нет');
$('.vk-friends').empty();
return;

<?else?>

const USER_ID = '$_USER_ID$';
const CACHE_KEY = 'friends_' + USER_ID;
const CACHE_TIME_KEY = CACHE_KEY + '_time';
const CACHE_TTL = 24*60*60*1000;

const $container = $('.vk-friends');
const $counter = $('#friends-count');
const DEFAULT_AVA = '/.s/src/profile/img/profile_photo_thumbnail.png';

let allFriends = [];

/* ==== MINI GRID ==== */
function renderMini(friends){
const onlineHtml = $('#uc-online-list').text().toLowerCase(); // список онлайн
$container.empty();
friends.slice(0,6).forEach(f=>{
const isOnline = onlineHtml.includes(f.nick.toLowerCase());
const statusDot = `<span class="statusDot ${isOnline ? 'onlineDot' : 'offlineDot'}"></span>`;

const card = $(`
<div class="vk-friend" title="${f.nick}">
<img src="${f.ava}">
<span>${f.nick}</span>
${statusDot}
</div>
`);
card.on('click',()=>location.href=f.profile);
$container.append(card);
});
$counter.html(`${friends.length} друзей`);
}

/* ==== POPUP ==== */
function showPopup(){
if(!allFriends.length) return;

const overlay = $('<div class="vk-ui-overlay"></div>');

const popup = $(`
<div class="vk-ui-popup">
<div class="vk-ui-header">
<div class="vk-ui-title">
Друзья <span class="vk-ui-title-count">${allFriends.length}</span>
</div>
<div class="vk-ui-search">
<input type="text" placeholder="Поиск друзей" id="friend-search">
</div>
</div>
<div class="vk-ui-list"></div>
</div>
`);

const list = popup.find('.vk-ui-list');
const onlineHtml = $('#uc-online-list').text().toLowerCase();

function renderList(filter=''){
list.empty();
allFriends
.filter(f => f.nick.toLowerCase().includes(filter.toLowerCase()))
.forEach(f=>{
let badge = 'Без статуса', cls = 'nostatus', letter = '';
const g = f.group.toLowerCase();
if(g.includes('кумир')) { badge='Кумир'; cls='idol'; letter='К'; }
else if(g.includes('друг')) { badge='Друг'; cls='friend'; letter='Д'; }
else if(g.includes('сем')) { badge='Семья'; cls='family'; letter='С'; }
else if(g.includes('знаком')) { badge='Знакомый'; cls='acquaintance'; letter='З'; }
else if(g.includes('прият')) { badge='Приятель'; cls='buddy'; letter='П'; }
else { badge = f.group || 'Без статуса'; cls='nostatus'; letter=''; }

const isOnline = onlineHtml.includes(f.nick.toLowerCase());
const statusBadge = isOnline
? '<div class="statusOnline">ONLINE</div>'
: '<div class="statusOffline">OFFLINE</div>';

const row = $(`
<div class="vk-ui-row ${cls}">
<div class="vk-ui-ava">
<img src="${f.ava}">
${letter ? `<span class="vk-ui-letter ${cls}">${letter}</span>` : ''}
${statusBadge}
</div>
<div class="vk-ui-info">
<div class="vk-ui-name">${f.nick}</div>
<div class="vk-ui-badge ${cls}">${badge}</div>
</div>
<div class="vk-ui-arrow">›</div>
</div>
`);
row.on('click',()=>location.href=f.profile);
list.append(row);
});
}

renderList();

popup.find('#friend-search').on('input', function(){
renderList($(this).val());
});

overlay.on('click', e => { if(e.target===overlay[0]) overlay.remove(); });
overlay.append(popup);
$('body').append(overlay);
}

/* ==== LOAD FRIENDS ==== */
function fetchFriends(){
const cached = localStorage.getItem(CACHE_KEY);
const time = localStorage.getItem(CACHE_TIME_KEY);

if(cached && time && Date.now()-time < CACHE_TTL){
allFriends = JSON.parse(cached);
renderMini(allFriends);
}

$.get('/blog/0-0-1-0-17-'+USER_ID, html=>{
const $tmp = $('<div>').html(html);
allFriends = [];
$tmp.find('.friend').each(function(){
const $el=$(this);
let group = $el.find('.gr').text().trim() || 'Без статуса';
const g = group.toLowerCase();
if(!['друг','кумир','семья','знакомый','приятель'].some(k => g.includes(k))) {
group = 'Приятель';
}

allFriends.push({
nick: $el.find('.nick').text().trim(),
ava: $el.find('.ava').text().trim() || DEFAULT_AVA,
profile: $el.find('.url').text().trim(),
group: group
});
});
localStorage.setItem(CACHE_KEY, JSON.stringify(allFriends));
localStorage.setItem(CACHE_TIME_KEY, Date.now());
renderMini(allFriends);
});
}

$counter.css('cursor','pointer').attr('title','Показать всех друзей').on('click', showPopup);
fetchFriends();

<?endif?>

});
</script>




Код
Небольшие детали ,  такие как статусы, поиск друзей или разделение на активных и неактивных пользователей — на самом деле делают модуль информативным, а значит, продвинутым. На платформе Юкоз с этим всегда были сложности, поэтому приходится использовать различные хаки, или, проще говоря, «костыли».

Мурчанн

Признаюсь, не знаю почему, но глядя на звезды мне всегда хочется мечтать.
Дата: Четверг, 08.01.2026, 23:45 | Сообщение # 2 | | Написал: Известный
Автор темы
Мурчанн не в сети
        Сообщений:288
         Регистрация:20.10.2016
За уважение окружающих За уважение окружающих За хорошее поведение За преданность форуму За любовь народа

Этот вариант отображения онлайн-статуса использует стандартную точку. Решение максимально простое, широко применяется на многих сайтах, а в скрипт были внесены лишь минимальные изменения.



Код
<!-- MINI BLOCK ДРУЗЬЯ -->
<div class="vk-old-block">
<div class="vk-old-header">Друзья</div>
<div class="vk-old-footer" id="friends-count">0 друзей</div>
<div class="vk-old-content">
<div class="vk-friends"></div>
</div>
</div>

<style>
/* ================= MINI BLOCK ================= */
.vk-old-block {
max-width:420px;
background:#fff;
border:1px solid #d1d5da;
font-family:Tahoma,Arial,sans-serif;
font-size:12px;
border-radius:16px;
overflow:hidden;
}

.vk-old-header {
background:linear-gradient(180deg,#5d82b9,#4a6fa8);
color:#fff;
font-weight:bold;
font-size:16px;
padding:10px 12px;
text-align:left;
border-bottom:1px solid #d1d5da;
border-top-left-radius:16px;
border-top-right-radius:16px;
}

.vk-old-content {
padding:10px;
}

.vk-old-footer {
background:#f7f7f7;
border-top:1px solid #d1d5da;
padding:6px 12px;
color:#666;
font-size:12px;
cursor:pointer;
text-align:left;
border-bottom-left-radius:0px;
border-bottom-right-radius:0px;
}

/* ================= MINI FRIENDS GRID ================= */
.vk-friends {
display:grid;
grid-template-columns:repeat(3, 1fr);
gap:10px 10px;
justify-items:center;
}

.vk-friend {
text-align:center;
cursor:pointer;
}

.vk-friend img {
width:82px;
height:82px;
border-radius:50%;
object-fit:cover;
}

.vk-friend span {
display:block;
margin-top:4px;
font-size:12px;
color:#1d2c45;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
}

/* При наведении — слегка больше яркости */
.vk-friend:hover {
filter: brightness(1.45); /* затемнение */
transform: scale(1.00); /* лёгкое увеличение */
}

/* ================= OVERLAY ================= */
.vk-ui-overlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.15);
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
}

.vk-ui-popup {
width: 400px; /* чуть уже */
max-height: 190vh; /* максимум по высоте экрана */
height: auto; /* автоматическая высота */
background: #f5f6f8;
border-radius: 24px;
display: flex;
flex-direction: column;
overflow: visible; /* важно */
box-shadow: 0 30px 70px rgba(0,0,0,0.25);
font-family: system-ui, -apple-system, Arial, sans-serif;
border: 1px solid #d1d5da;
padding: 0;
}

/* ================= HEADER ================= */
.vk-ui-header {
padding: 18px 24px;
display: flex;
align-items: center;
justify-content: space-between; /* "Друзья" слева, поиск справа */
background: rgba(93,130,185,0.9); /* стеклянный синий */
backdrop-filter: blur(14px) saturate(180%) brightness(1.05);
-webkit-backdrop-filter: blur(14px) saturate(180%) brightness(1.05);
border-bottom: 1px solid rgba(0,0,0,0.12);
border-top-left-radius: 24px;
border-top-right-radius: 24px;
}

/* Заголовок "Друзья" */
.vk-ui-title {
font-size: 20px;
font-weight: 600;
margin: 0;
color: #ffffff;
}

/* Поиск справа */
.vk-ui-search {
flex-shrink: 0;
}

.vk-ui-search input {
width: 160px;
padding: 8px 14px 8px 36px; /* минимальные отступы */
border-radius: 14px;
border: none;
outline: none;
font-size: 15px;
background: rgba(255,255,255,0.95);
color: #1d2c45;
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' fill='%2390a4c0'%3E%3Cpath d='M17.5 16l-4.3-4.3a7 7 0 1 0-1.4 1.4L16 17.5zM7 12a5 5 0 1 1 0-10 5 5 0 0 1 0 10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: 10px center;
background-size: 18px 18px;
}

/* ================= INNER BLOCK ================= */
.vk-ui-inner {
background: #f9f9f9; /* светло-серый фон для внутреннего блока */
padding: 14px; /* отступы внутри popup */
}

/* Внутренний белый блок */
.vk-ui-inner .vk-ui-subblock {
background: #ffffff; /* сам белый блок внутри */
padding: 12px; /* отступы внутри блока */
border-radius: 12px;
border: 1px solid #e4e8f0;
margin-top: 12px; /* небольшой отступ от верхнего серого фона */
}

/* ================= FRIEND LIST ================= */
.vk-ui-list {
max-height: 62vh; /* больше места */
overflow-y: auto; /* прокрутка внутри */
padding: 12px;
margin: 12px;
background: #fff;
border-radius: 12px;
border: 1px solid #e4e8f0;
box-sizing: border-box;
flex: 1; /* тянется на всю доступную высоту */
}

/* Скрываем скролл для Webkit-браузеров (Chrome, Edge, Safari) */
.vk-ui-list::-webkit-scrollbar {
width: 0;
height: 0;
}

/* ROW */
.vk-ui-row {
display: flex;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid #e4e8f0;
cursor: pointer;
transition: background 0.2s ease;
}

.vk-ui-row:last-child {
border-bottom: none;
}

.vk-ui-row:hover {
background: #f5f7fb;
}

/* =========================
AVATAR + ONLINE STATUS
========================= */
.vk-ui-ava {
position: relative;
width: 88px;
height: 88px;
margin-bottom: 8px; /* расстояние под аватаркой до текста/статуса */
}

/* Аватарка */
.vk-ui-ava img {
width: 88px;
height: 88px;
border-radius: 14px;
object-fit: cover;
box-shadow: 0 4px 14px rgba(0,0,0,0.25);
}

/* STATUS LETTER (кружок с буквой) */
.vk-ui-letter.friend { background: #6b93c7; } /* Друг */
.vk-ui-letter.idol { background: #c84b4b; } /* Кумир */
.vk-ui-letter.family { background: #4a9e4a; } /* Семья */
.vk-ui-letter.acquaintance { background: #8a8a8a; }/* Знакомый */
.vk-ui-letter.buddy { background: #d18c3b; } /* Приятель */
.vk-ui-letter.nostatus { background: #bbbbbb; } /* Без статуса */

/* BADGE (текстовый статус) */
.vk-ui-badge.friend { background: #6b93c7; }
.vk-ui-badge.idol { background: #c84b4b; }
.vk-ui-badge.family { background: #4a9e4a; }
.vk-ui-badge.acquaintance { background: #8a8a8a; }
.vk-ui-badge.buddy { background: #d18c3b; }
.vk-ui-badge.nostatus { background: #bbbbbb; }

/* STATUS LETTER-2 */
.vk-ui-letter {
position: absolute;
bottom: -4px;
left: -4px;
width: 26px;
height: 26px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 14px;
color: #fff;
}

.vk-ui-letter.friend { background: #6b93c7; }
.vk-ui-letter.idol { background: #c84b4b; }

/* INFO */
.vk-ui-info {
flex: 1;
margin-left: 14px;
}

.vk-ui-name {
font-size: 20px;
font-weight: 600;
color: #1d2c45;
}

.vk-ui-badge {
margin-top: 6px;
padding: 6px 14px;
border-radius: 999px;
font-size: 14px;
display: inline-block;
color: #fff;
}

.vk-ui-badge.friend { background: #6b93c7; }
.vk-ui-badge.idol { background: #c84b4b; }
.vk-ui-badge.nostatus { background: #8a8a8a; }

/* ARROW */
.vk-ui-arrow {
font-size: 28px;
color: #b6bfd6;
}

/* Кружок с числом друзей */
.vk-ui-title-count {
display: inline-flex;
align-items: center;
justify-content: center;
width: 27px; /* размер кружка */
height: 27px;
margin-left: 0px; /* отступ от слова "Друзья" */
border-radius: 50%;
background: #fff; /* белый фон */
border: 1px solid #7e7f80; /* белая обводка */
color: #7e7f80; /* серый цвет текста */
font-size: 14px;
font-weight: 700;
}

/* ==============================
Мини-грид — точки на аватарке (только онлайн)
============================== */
.vk-friend {
position: relative; /* точка позиционируется относительно аватарки */
display: inline-block;
}

.vk-friend img {
border-radius: 50%; /* аватарка круглая */
display: block;
}

/* ==== Точка онлайн ==== */
.vk-friend .statusDot {
position: absolute;
width: 16px; /* размер точки */
height: 16px;
border-radius: 50%; /* круглая точка */
border: 2px solid #fff; /* белая обводка */
box-shadow: 0 0 2px rgba(0,0,0,0.5);

bottom: 18px; /* вертикальное положение */
right: 12px; /* смещение вправо */
}

/* Цвет точки только для онлайн */
.vk-friend .onlineDot {
background-color: #3eb749; /* зелёная */
}

/* Убираем точку, если офлайн */
.vk-friend .offlineDot {
display: none;
}

/* ==============================
Всплывающее окно — аватар и статус
============================== */
.vk-ui-ava {
text-align: center;
position: relative;
margin-bottom: 12px; /* отступ под аватаркой важно */
}

/* ==== Точка статуса во всплывающем окне ==== */
.vk-ui-ava .statusDot {
position: absolute;
width: 14px; /* размер точки */
height: 14px;
border-radius: 50%;
border: 2px solid #fff;
box-shadow: 0 0 2px rgba(0,0,0,0.5);

bottom: 6px; /* регулируем вертикальное положение */
right: 6px; /* смещение вправо */
}

/* Цвет точки по статусу */
.vk-ui-ava .onlineDot { background-color: #3eb749; } /* онлайн зелёная */
.vk-ui-ava .offlineDot { background-color: #ff3b3b; } /* офлайн красная */

/* ==== Статус текстом под аватаркой ==== */
.vk-ui-ava .statusOnline {
display: block;
margin: 6px auto 0 auto; /* вертикальное положение, можно регулировать */
padding: 2px 8px;
font-size: 10px;
font-weight: bold;
text-transform: uppercase;
color: #fff;
background-color: #3eb749;
border-radius: 4px;
text-align: center;
width: fit-content;
}

.vk-ui-ava .statusOffline {
display: block;
margin: 6px auto 0 auto; /* вертикальное положение, можно регулировать */
padding: 2px 8px;
font-size: 10px;
font-weight: bold;
text-transform: uppercase;
color: #fff;
background-color: #ff3b3b;
border-radius: 4px;
text-align: center;
width: fit-content;
}

/* ==============================
Настройки
============================== */
/* Размер точки */
.vk-ui-ava .statusDot { width: 18px; height: 18px; }

/* Смещение точки */
.vk-ui-ava .statusDot { bottom: -1px; right: -3px; } /* смещаем статус вверх или вниз */
</style>

<!-- Скрытый контейнер со списком онлайн-пользователей -->
<div id="uc-online-list" style="display:none;">
$ONLINE_USERS_LIST$
</div>

<script>
$(function(){

<?if(!$USER_LOGGED_IN$)?>

$('#friends-count').text('Друзей нет');
$('.vk-friends').empty();
return;

<?else?>

const USER_ID = '$_USER_ID$';
const CACHE_KEY = 'friends_' + USER_ID;
const CACHE_TIME_KEY = CACHE_KEY + '_time';
const CACHE_TTL = 24*60*60*1000;

const $container = $('.vk-friends');
const $counter = $('#friends-count');
const DEFAULT_AVA = '/.s/src/profile/img/profile_photo_thumbnail.png';

let allFriends = [];

/* ==== MINI GRID ==== */
function renderMini(friends){
const onlineHtml = $('#uc-online-list').text().toLowerCase(); // список онлайн
$container.empty();
friends.slice(0,6).forEach(f=>{
const isOnline = onlineHtml.includes(f.nick.toLowerCase());
const statusDot = `<span class="statusDot ${isOnline ? 'onlineDot' : 'offlineDot'}"></span>`;

const card = $(`
<div class="vk-friend" title="${f.nick}">
<img src="${f.ava}">
<span>${f.nick}</span>
${statusDot}
</div>
`);
card.on('click',()=>location.href=f.profile);
$container.append(card);
});
$counter.html(`${friends.length} друзей`);
}

/* ==== POPUP ==== */
function showPopup(){
if(!allFriends.length) return;

const overlay = $('<div class="vk-ui-overlay"></div>');

const popup = $(`
<div class="vk-ui-popup">
<div class="vk-ui-header">
<div class="vk-ui-title">
Друзья <span class="vk-ui-title-count">${allFriends.length}</span>
</div>
<div class="vk-ui-search">
<input type="text" placeholder="Поиск друзей" id="friend-search">
</div>
</div>
<div class="vk-ui-list"></div>
</div>
`);

const list = popup.find('.vk-ui-list');
const onlineHtml = $('#uc-online-list').text().toLowerCase();

function renderList(filter=''){
list.empty();
allFriends
.filter(f => f.nick.toLowerCase().includes(filter.toLowerCase()))
.forEach(f=>{
let badge = 'Без статуса', cls = 'nostatus', letter = '';
const g = f.group.toLowerCase();
if(g.includes('кумир')) { badge='Кумир'; cls='idol'; letter='К'; }
else if(g.includes('друг')) { badge='Друг'; cls='friend'; letter='Д'; }
else if(g.includes('сем')) { badge='Семья'; cls='family'; letter='С'; }
else if(g.includes('знаком')) { badge='Знакомый'; cls='acquaintance'; letter='З'; }
else if(g.includes('прият')) { badge='Приятель'; cls='buddy'; letter='П'; }
else { badge = f.group || 'Без статуса'; cls='nostatus'; letter=''; }

const isOnline = onlineHtml.includes(f.nick.toLowerCase());
const statusBadge = `<span class="statusDot ${isOnline ? 'onlineDot' : 'offlineDot'}"></span>`;

const row = $(`
<div class="vk-ui-row ${cls}">
<div class="vk-ui-ava">
<img src="${f.ava}">
${letter ? `<span class="vk-ui-letter ${cls}">${letter}</span>` : ''}
${statusBadge}
</div>
<div class="vk-ui-info">
<div class="vk-ui-name">${f.nick}</div>
<div class="vk-ui-badge ${cls}">${badge}</div>
</div>
<div class="vk-ui-arrow">›</div>
</div>
`);
row.on('click',()=>location.href=f.profile);
list.append(row);
});
}

renderList();

popup.find('#friend-search').on('input', function(){
renderList($(this).val());
});

overlay.on('click', e => { if(e.target===overlay[0]) overlay.remove(); });
overlay.append(popup);
$('body').append(overlay);
}

/* ==== LOAD FRIENDS ==== */
function fetchFriends(){
const cached = localStorage.getItem(CACHE_KEY);
const time = localStorage.getItem(CACHE_TIME_KEY);

if(cached && time && Date.now()-time < CACHE_TTL){
allFriends = JSON.parse(cached);
renderMini(allFriends);
}

$.get('/blog/0-0-1-0-17-'+USER_ID, html=>{
const $tmp = $('<div>').html(html);
allFriends = [];
$tmp.find('.friend').each(function(){
const $el=$(this);
let group = $el.find('.gr').text().trim() || 'Без статуса';
const g = group.toLowerCase();
if(!['друг','кумир','семья','знакомый','приятель'].some(k => g.includes(k))) {
group = 'Приятель';
}

allFriends.push({
nick: $el.find('.nick').text().trim(),
ava: $el.find('.ava').text().trim() || DEFAULT_AVA,
profile: $el.find('.url').text().trim(),
group: group
});
});
localStorage.setItem(CACHE_KEY, JSON.stringify(allFriends));
localStorage.setItem(CACHE_TIME_KEY, Date.now());
renderMini(allFriends);
});
}

$counter.css('cursor','pointer').attr('title','Показать всех друзей').on('click', showPopup);
fetchFriends();

<?endif?>

});
</script>

Мурчанн

Признаюсь, не знаю почему, но глядя на звезды мне всегда хочется мечтать.
  • Страница 1 из 1
  • 1
Поиск: