Додані повідомлення та перепрацьована структура застосунку та api
This commit is contained in:
234
web/lib/customElements/swipeUpdater.js
Normal file
234
web/lib/customElements/swipeUpdater.js
Normal file
@@ -0,0 +1,234 @@
|
||||
/**
|
||||
* Вебкомпонент для ініціації оновлення сторінки (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();
|
||||
|
||||
*/
|
||||
Reference in New Issue
Block a user