Додан фільтр на сторінці керування вісниками.

Виправлено помилки.
This commit is contained in:
2026-06-07 01:16:17 +03:00
parent 49b559f03e
commit 0b38f137bf
6 changed files with 160 additions and 49 deletions

View File

@@ -19,6 +19,8 @@ class SwipeUpdater extends HTMLElement {
// 3. Внутрішній стан
this._isReadyToReload = false; // Прапорець, що вказує на готовність до оновлення
this._isStandalone = false; // Кеш для перевірки PWA режиму
this._isTouching = false; // Чи тримає користувач палець на екрані
// 4. Створення елементів (Внутрішній HTML)
shadow.innerHTML = `
@@ -111,8 +113,10 @@ class SwipeUpdater extends HTMLElement {
this._animID = shadow.getElementById('swipe_updater');
this._animIconID = shadow.getElementById('swipe_icon');
// 6. Прив'язка контексту `this` для обробника подій (важливо для коректної роботи `this.handleScroll`)
// 6. Прив'язка контексту `this` для обробників подій
this.handleScroll = this.handleScroll.bind(this);
this.handleTouchStart = this.handleTouchStart.bind(this);
this.handleTouchEnd = this.handleTouchEnd.bind(this);
}
/**
@@ -128,51 +132,81 @@ class SwipeUpdater extends HTMLElement {
}
}
// Фіксуємо, що користувач торкнувся екрана
handleTouchStart() {
this._isTouching = true;
}
// Обробка події, коли користувач відпускає екран
handleTouchEnd() {
this._isTouching = false;
const threshold = -165;
// Якщо палець відпустили, а ефект «гумового скролу» вже повернувся вище порогу
// (тобто користувач передумав і потягнув назад вгору перед тим як відпустити)
if (window.scrollY > threshold && this._isReadyToReload) {
this._resetRefreshState();
}
}
/**
* Обробник події скролу (головна логіка Pull-to-Refresh).
* Відстежує прокручування вище верхньої межі сторінки (`window.scrollY < 0`).
*/
handleScroll() {
// Перевірка на режим Standalone (PWA) - функціональність актуальна переважно тут
const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
if (!this._isStandalone) return;
if (isStandalone) {
let scrollY = window.scrollY;
// 1. Анімація іконки під час прокручування за верхню межу (scrollY < 0)
if (scrollY <= -10) {
// Зміщення іконки разом із прокруткою
this._animIconID.style.top = `${scrollY / 1.5}px`;
this._animID.style.zIndex = '115'; // Піднімаємо z-index для видимості над контентом
} else {
this._animID.style.zIndex = '0'; // Повертаємо базовий z-index
const scrollY = window.scrollY;
const threshold = -165; // Поріг прокрутки (наприклад, -125px) для активації оновлення
// 1. Анімація іконки під час прокручування за верхню межу (scrollY < 0)
if (scrollY <= -10) {
// Зміщення іконки разом із прокруткою
this._animIconID.style.top = `${scrollY / 1.5}px`;
this._animID.style.zIndex = '115'; // Піднімаємо z-index для видимості над контентом
} else {
this._animIconID.style.top = '0px';
this._animID.style.zIndex = '0'; // Повертаємо базовий z-index
}
// 2. Логіка активації "готовий до оновлення"
if (scrollY <= threshold) {
if (!this._isReadyToReload) {
this._isReadyToReload = true;
// Поворот іконки на 180 градусів
this._animIconID.style.transform = 'rotate(180deg)';
this._animIconID.setAttribute('data-state', ''); // Деактивація стану "active"
}
const threshold = -125; // Поріг прокрутки (наприклад, -125px) для активації оновлення
// 2. Логіка активації "готовий до оновлення"
if (scrollY <= threshold) {
if (!this._isReadyToReload) {
this._isReadyToReload = true;
// Поворот іконки на 180 градусів
this._animIconID.style.transform = 'rotate(180deg)';
this._animIconID.setAttribute('data-state', ''); // Деактивація стану "active"
}
}
// 3. Логіка виклику оновлення та скидання стану
// Якщо користувач відпускає свайп (scrollY повертається до >= 0) І був готовий до оновлення
else if (scrollY >= 0) {
if (this._isReadyToReload) {
// Виклик користувацької функції (або стандартного window.location.reload())
this._appReload();
// Скидання стану та анімації
this._isReadyToReload = false;
this._animIconID.style.transform = 'rotate(0deg)';
this._animIconID.setAttribute('data-state', 'active');
}
}
// Скасування жесту під час руху пальцем вгору (ще до відпускання)
else if (scrollY > threshold && scrollY < 0 && this._isTouching) {
if (this._isReadyToReload) {
this._isReadyToReload = false;
this._animIconID.style.transform = 'rotate(0deg)';
this._animIconID.setAttribute('data-state', 'active');
}
}
// 3. Логіка виклику оновлення, коли сторінка повернулась до нуля
else if (scrollY >= 0) {
if (this._isReadyToReload) {
this._appReload();
this._resetRefreshState();
}
}
}
/**
* Допоміжний метод для повного скидання графічного стану
*/
_resetRefreshState() {
this._isReadyToReload = false;
this._animIconID.style.transform = 'rotate(0deg)';
this._animIconID.setAttribute('data-state', 'active');
this._animIconID.style.top = '0px';
this._animID.style.zIndex = '0';
}
@@ -181,8 +215,15 @@ class SwipeUpdater extends HTMLElement {
* Додаємо обробник події прокручування.
*/
connectedCallback() {
// Прослуховування глобальної події скролу
window.addEventListener('scroll', this.handleScroll);
// Перевіряємо PWA режим ОДИН раз при додаванні компонента на сторінку
this._isStandalone = window.matchMedia('(display-mode: standalone)').matches;
if (this._isStandalone) {
window.addEventListener('scroll', this.handleScroll);
// passive: true покращує продуктивність скролу на мобільних пристроях
window.addEventListener('touchstart', this.handleTouchStart, { passive: true });
window.addEventListener('touchend', this.handleTouchEnd, { passive: true });
}
}
/**
@@ -190,12 +231,17 @@ class SwipeUpdater extends HTMLElement {
* Видаляємо обробник події прокручування для запобігання витоку пам'яті.
*/
disconnectedCallback() {
// Чистимо абсолютно всі додані слухачі
window.removeEventListener('scroll', this.handleScroll);
window.removeEventListener('touchstart', this.handleTouchStart);
window.removeEventListener('touchend', this.handleTouchEnd);
}
}
// Реєстрація веб-компонента
customElements.define('swipe-updater', SwipeUpdater);
if (!customElements.get('swipe-updater')) {
customElements.define('swipe-updater', SwipeUpdater);
}
/*