Files
Sheep-Service/web/lib/customElements/swipeUpdater.js

234 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Вебкомпонент для ініціації оновлення сторінки (Pull-to-Refresh)
* за допомогою свайпу вниз на пристроях з iOS/iPadOS у режимі PWA.
*/
class SwipeUpdater extends HTMLElement {
constructor() {
super();
// 1. Створення Shadow DOM
// Використовуємо тіньовий DOM для інкапсуляції стилів та структури
const shadow = this.attachShadow({ mode: 'open' });
// 2. Внутрішня функція оновлення за замовчуванням
this._appReload = () => {
console.log('Стандартна функція: Перезавантаження сторінки');
// Стандартна дія - перезавантаження сторінки
window.location.reload();
};
// 3. Внутрішній стан
this._isReadyToReload = false; // Прапорець, що вказує на готовність до оновлення
// 4. Створення елементів (Внутрішній HTML)
shadow.innerHTML = `
<div id="swipe_updater">
<div id="swipe_block">
<svg id="swipe_icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-state="active">
<path d="M413.1 222.5l22.2 22.2c9.4 9.4 9.4 24.6 0 33.9L241 473c-9.4 9.4-24.6 9.4-33.9 0L12.7 278.6c-9.4-9.4-9.4-24.6 0-33.9l22.2-22.2c9.5-9.5 25-9.3 34.3.4L184 343.4V56c0-13.3 10.7-24 24-24h32c13.3 0 24 10.7 24 24v287.4l114.8-120.5c9.3-9.8 24.8-10 34.3-.4z"></path>
</svg>
</div>
</div>
<style>
:host {
display: block; /* Важливо для позиціонування кореневого елемента */
}
#swipe_updater {
position: absolute;
top: 0px;
width: 100%;
z-index: 0; /* Базовий z-index */
/* Використання CSS-змінних для кастомізації кольорів */
--swipe-color-theme1: var(--ColorThemes2, #525151); /* Колір фону іконки */
--swipe-color-theme2: var(--ColorThemes3, #f3f3f3); /* Колір іконки та рамки */
}
#swipe_block {
/* Розрахунок ширини та відступу для центрифікації в певних макетах */
width: calc(100% - 252px);
margin-left: 252px;
height: 50px;
display: flex;
justify-content: center;
align-items: flex-end;
position: relative;
}
#swipe_icon {
width: 20px;
fill: var(--swipe-color-theme2);
transform: rotate(0deg); /* Початковий стан: стрілка вниз */
position: absolute;
/* Початкове приховане позиціонування */
margin-top: -30px;
top: -30px;
background: var(--swipe-color-theme1);
border: 2px solid var(--swipe-color-theme2);
border-radius: 50%;
padding: 10px;
display: flex;
overflow: hidden;
height: 0;
opacity: 0;
/* Анімація: прихована іконка плавно з'являється (активується/деактивується) */
transition: height 0ms 450ms, opacity 450ms 0ms, transform 450ms;
}
#swipe_icon[data-state="active"] {
height: 20px;
margin-top: -45px;
top: -45px;
opacity: 1;
/* Анімація: активна іконка видима */
transition: height 0ms 0ms, opacity 450ms 0ms, transform 450ms;
}
/* Адаптивні стилі для зміни центрифікації на різних екранах */
@media (max-width: 1100px){
#swipe_block {
width: calc(100% - 122px);
margin-left: 122px;
}
}
@media (max-width: 700px), (max-height: 540px) {
#swipe_block {
width: 100%;
margin-left: 0;
}
/* Зміна кольорів для менших екранів */
#swipe_updater {
--swipe-color-theme1: var(--ColorThemes0, #525151);
--swipe-color-theme2: var(--ColorThemes3, #f3f3f3);;
}
}
</style>
`;
// 5. Збереження посилань на елементи Shadow DOM
this._animID = shadow.getElementById('swipe_updater');
this._animIconID = shadow.getElementById('swipe_icon');
// 6. Прив'язка контексту `this` для обробника подій (важливо для коректної роботи `this.handleScroll`)
this.handleScroll = this.handleScroll.bind(this);
}
/**
* Метод для встановлення користувацької функції оновлення.
* Замінює стандартне перезавантаження сторінки.
* @param {function} func - Користувацька функція, що буде викликана при свайпі.
*/
setReloadFunction(func) {
if (typeof func === 'function') {
this._appReload = func;
} else {
console.error('setReloadFunction вимагає передати функцію.');
}
}
/**
* Обробник події скролу (головна логіка Pull-to-Refresh).
* Відстежує прокручування вище верхньої межі сторінки (`window.scrollY < 0`).
*/
handleScroll() {
// Перевірка на режим Standalone (PWA) - функціональність актуальна переважно тут
const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
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 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');
}
}
}
}
/**
* Lifecycle hook: викликається при додаванні елемента в DOM.
* Додаємо обробник події прокручування.
*/
connectedCallback() {
// Прослуховування глобальної події скролу
window.addEventListener('scroll', this.handleScroll);
}
/**
* Lifecycle hook: викликається при видаленні елемента з DOM.
* Видаляємо обробник події прокручування для запобігання витоку пам'яті.
*/
disconnectedCallback() {
window.removeEventListener('scroll', this.handleScroll);
}
}
// Реєстрація веб-компонента
customElements.define('swipe-updater', SwipeUpdater);
/*
============================
ПРИКЛАД ВИКОРИСТАННЯ
============================
*/
/*
1. Додайте цей елемент у свій HTML:
<swipe-updater id="swipe-updater"></swipe-updater>
2. Отримайте посилання на компонент у JS:
const Updater = document.querySelector('swipe-updater');
3. Користувацька функція оновлення
function customReload() {
const now = new Date().toLocaleTimeString();
console.log(`Користувацьке оновлення: оновлено о ${now}`);
document.querySelector('h1').textContent = `Сторінка оновлена о ${now}`;
// Тут можна виконати AJAX-запит, оновити DOM тощо.
}
4. Перевизначення функції оновлення компонента
if (Updater && Updater.setReloadFunction) {
Updater.setReloadFunction(customReload);
} else {
console.error('Компонент SwipeUpdater не знайдено або не готовий.');
}
💡 Приклад стандартного використання (якщо не викликати setReloadFunction):
<swipe-updater></swipe-updater>
При свайпі буде викликано window.location.reload();
*/