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

1
Админ
Постов: 162
2
Элита
Постов: 45
3
VIP
Постов: 35
4
Проверенные
Постов: 31
5
Проверенные
Постов: 30
6
Пользователи
Постов: 27
7
VIP
Постов: 26
8
Пользователи
Постов: 24

  • Страница 1 из 1
  • 1
Статус онлайн модуль ВК «Друзья» для Ucoz v 6.0
Дата: Четверг, 08.01.2026, 14:04 | Сообщение # 1 | | Написал: Узнаваемый
Автор темы
Мурчанн не в сети
        Сообщений:162
         Регистрация: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 | | Написал: Узнаваемый
Автор темы
Мурчанн не в сети
        Сообщений:162
         Регистрация: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
Поиск: