Додан фільтр на сторінці керування вісниками.
Виправлено помилки.
This commit is contained in:
@@ -19,6 +19,8 @@ class SwipeUpdater extends HTMLElement {
|
|||||||
|
|
||||||
// 3. Внутрішній стан
|
// 3. Внутрішній стан
|
||||||
this._isReadyToReload = false; // Прапорець, що вказує на готовність до оновлення
|
this._isReadyToReload = false; // Прапорець, що вказує на готовність до оновлення
|
||||||
|
this._isStandalone = false; // Кеш для перевірки PWA режиму
|
||||||
|
this._isTouching = false; // Чи тримає користувач палець на екрані
|
||||||
|
|
||||||
// 4. Створення елементів (Внутрішній HTML)
|
// 4. Створення елементів (Внутрішній HTML)
|
||||||
shadow.innerHTML = `
|
shadow.innerHTML = `
|
||||||
@@ -111,8 +113,10 @@ class SwipeUpdater extends HTMLElement {
|
|||||||
this._animID = shadow.getElementById('swipe_updater');
|
this._animID = shadow.getElementById('swipe_updater');
|
||||||
this._animIconID = shadow.getElementById('swipe_icon');
|
this._animIconID = shadow.getElementById('swipe_icon');
|
||||||
|
|
||||||
// 6. Прив'язка контексту `this` для обробника подій (важливо для коректної роботи `this.handleScroll`)
|
// 6. Прив'язка контексту `this` для обробників подій
|
||||||
this.handleScroll = this.handleScroll.bind(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).
|
* Обробник події скролу (головна логіка Pull-to-Refresh).
|
||||||
* Відстежує прокручування вище верхньої межі сторінки (`window.scrollY < 0`).
|
* Відстежує прокручування вище верхньої межі сторінки (`window.scrollY < 0`).
|
||||||
*/
|
*/
|
||||||
handleScroll() {
|
handleScroll() {
|
||||||
// Перевірка на режим Standalone (PWA) - функціональність актуальна переважно тут
|
// Перевірка на режим Standalone (PWA) - функціональність актуальна переважно тут
|
||||||
const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
|
if (!this._isStandalone) return;
|
||||||
|
|
||||||
if (isStandalone) {
|
|
||||||
let scrollY = window.scrollY;
|
|
||||||
|
|
||||||
// 1. Анімація іконки під час прокручування за верхню межу (scrollY < 0)
|
const scrollY = window.scrollY;
|
||||||
if (scrollY <= -10) {
|
const threshold = -165; // Поріг прокрутки (наприклад, -125px) для активації оновлення
|
||||||
// Зміщення іконки разом із прокруткою
|
|
||||||
this._animIconID.style.top = `${scrollY / 1.5}px`;
|
// 1. Анімація іконки під час прокручування за верхню межу (scrollY < 0)
|
||||||
this._animID.style.zIndex = '115'; // Піднімаємо z-index для видимості над контентом
|
if (scrollY <= -10) {
|
||||||
} else {
|
// Зміщення іконки разом із прокруткою
|
||||||
this._animID.style.zIndex = '0'; // Повертаємо базовий z-index
|
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) для активації оновлення
|
// Скасування жесту під час руху пальцем вгору (ще до відпускання)
|
||||||
|
else if (scrollY > threshold && scrollY < 0 && this._isTouching) {
|
||||||
// 2. Логіка активації "готовий до оновлення"
|
if (this._isReadyToReload) {
|
||||||
if (scrollY <= threshold) {
|
this._isReadyToReload = false;
|
||||||
if (!this._isReadyToReload) {
|
this._animIconID.style.transform = 'rotate(0deg)';
|
||||||
this._isReadyToReload = true;
|
this._animIconID.setAttribute('data-state', 'active');
|
||||||
// Поворот іконки на 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');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 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() {
|
connectedCallback() {
|
||||||
// Прослуховування глобальної події скролу
|
// Перевіряємо PWA режим ОДИН раз при додаванні компонента на сторінку
|
||||||
window.addEventListener('scroll', this.handleScroll);
|
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() {
|
disconnectedCallback() {
|
||||||
|
// Чистимо абсолютно всі додані слухачі
|
||||||
window.removeEventListener('scroll', this.handleScroll);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
${butt_add}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ const Sheeps = {
|
|||||||
let html = await fetch('/lib/pages/sheeps/index.html').then((response) => response.text());
|
let html = await fetch('/lib/pages/sheeps/index.html').then((response) => response.text());
|
||||||
app.innerHTML = html;
|
app.innerHTML = html;
|
||||||
|
|
||||||
await Sheeps.sheeps_list.setHTML();
|
await Sheeps.sheeps_list.setHTML({});
|
||||||
if (id) Sheeps.editor.setHTML(id);
|
if (id) Sheeps.editor.setHTML(id);
|
||||||
|
|
||||||
SheepsEvents.init();
|
SheepsEvents.init();
|
||||||
@@ -182,7 +182,7 @@ const Sheeps = {
|
|||||||
|
|
||||||
return Sheeps.sheeps_list.list
|
return Sheeps.sheeps_list.list
|
||||||
},
|
},
|
||||||
setHTML: async (search_value = null) => {
|
setHTML: async ({search, filter}) => {
|
||||||
let block_sheep_list = document.getElementById('block-sheeps-list');
|
let block_sheep_list = document.getElementById('block-sheeps-list');
|
||||||
let block_sheep_info = document.getElementById('block-sheep-info');
|
let block_sheep_info = document.getElementById('block-sheep-info');
|
||||||
block_sheep_list.style.display = "flex";
|
block_sheep_list.style.display = "flex";
|
||||||
@@ -210,6 +210,9 @@ const Sheeps = {
|
|||||||
<div class="header">
|
<div class="header">
|
||||||
<h1>Всі вісники</h1>
|
<h1>Всі вісники</h1>
|
||||||
<div>
|
<div>
|
||||||
|
<button title="Фільтрувати" onclick="Sheeps.filter.open()">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" x="0" y="0" viewBox="0 0 24 24" xml:space="preserve" style="padding: 6px;"><g><path xmlns="http://www.w3.org/2000/svg" d="m21 2h-18a1.0007 1.0007 0 0 0 -.8193 1.5732l6.8193 9.7422v7.6846a1.0015 1.0015 0 0 0 1.53.8481l4-2.5a1.0014 1.0014 0 0 0 .47-.8481v-5.1846l6.8193-9.7422a1.0007 1.0007 0 0 0 -.8193-1.5732z"></path></g></svg>
|
||||||
|
</button>
|
||||||
<button title="Пошук" onclick="Sheeps.search.open()">
|
<button title="Пошук" onclick="Sheeps.search.open()">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72"><path d="M 31 11 C 19.973 11 11 19.973 11 31 C 11 42.027 19.973 51 31 51 C 34.974166 51 38.672385 49.821569 41.789062 47.814453 L 54.726562 60.751953 C 56.390563 62.415953 59.088953 62.415953 60.751953 60.751953 C 62.415953 59.087953 62.415953 56.390563 60.751953 54.726562 L 47.814453 41.789062 C 49.821569 38.672385 51 34.974166 51 31 C 51 19.973 42.027 11 31 11 z M 31 19 C 37.616 19 43 24.384 43 31 C 43 37.616 37.616 43 31 43 C 24.384 43 19 37.616 19 31 C 19 24.384 24.384 19 31 19 z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72"><path d="M 31 11 C 19.973 11 11 19.973 11 31 C 11 42.027 19.973 51 31 51 C 34.974166 51 38.672385 49.821569 41.789062 47.814453 L 54.726562 60.751953 C 56.390563 62.415953 59.088953 62.415953 60.751953 60.751953 C 62.415953 59.087953 62.415953 56.390563 60.751953 54.726562 L 47.814453 41.789062 C 49.821569 38.672385 51 34.974166 51 31 C 51 19.973 42.027 11 31 11 z M 31 19 C 37.616 19 43 24.384 43 31 C 43 37.616 37.616 43 31 43 C 24.384 43 19 37.616 19 31 C 19 24.384 24.384 19 31 19 z"/></svg>
|
||||||
</button>
|
</button>
|
||||||
@@ -224,6 +227,27 @@ const Sheeps = {
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
let filter_selected = Number(filter)
|
||||||
|
html += `
|
||||||
|
<div class="filter" id="block-sheeps-list-filter" data-state="closed">
|
||||||
|
<select title="Оберіть групу" onchange="Sheeps.filter.input(this.value)">
|
||||||
|
<option value="0" ${Number(filter) == 0 ? 'selected' : ''}>Всі</option>
|
||||||
|
<option value="1" ${Number(filter) === 1 ? 'selected' : ''}>Група 1</option>
|
||||||
|
<option value="2" ${Number(filter) === 2 ? 'selected' : ''}>Група 2</option>
|
||||||
|
<option value="3" ${Number(filter) === 3 ? 'selected' : ''}>Група 3</option>
|
||||||
|
<option value="4" ${Number(filter) === 4 ? 'selected' : ''}>Група 4</option>
|
||||||
|
<option value="5" ${Number(filter) === 5 ? 'selected' : ''}>Група 5</option>
|
||||||
|
<option value="6" ${Number(filter) === 6 ? 'selected' : ''}>Група 6</option>
|
||||||
|
<option value="7" ${Number(filter) === 7 ? 'selected' : ''}>Група 7</option>
|
||||||
|
<option value="8" ${Number(filter) === 8 ? 'selected' : ''}>Група 8</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
html += `
|
||||||
|
<br>
|
||||||
|
`;
|
||||||
|
|
||||||
const accessTemplate = (p) => {
|
const accessTemplate = (p) => {
|
||||||
let perms = [];
|
let perms = [];
|
||||||
if (p.can_view_sheeps) perms.push("View Sheeps");
|
if (p.can_view_sheeps) perms.push("View Sheeps");
|
||||||
@@ -242,7 +266,11 @@ const Sheeps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (const element of list) {
|
for (const element of list) {
|
||||||
if (search_value && !element.name.toLowerCase().includes(search_value)) {
|
if (search && !element.name.toLowerCase().includes(search)) {
|
||||||
|
continue; // пропустити, якщо ім'я не містить рядок пошуку
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter && element.group_id !== Number(filter) && Number(filter) !== 0) {
|
||||||
continue; // пропустити, якщо ім'я не містить рядок пошуку
|
continue; // пропустити, якщо ім'я не містить рядок пошуку
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,18 +474,42 @@ const Sheeps = {
|
|||||||
}, 10)
|
}, 10)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
filter: {
|
||||||
|
open: () => {
|
||||||
|
const sheepFilterForm = document.getElementById("block-sheeps-list-filter");
|
||||||
|
const current = sheepFilterForm.dataset.state;
|
||||||
|
|
||||||
|
if(current === 'closed') Sheeps.search.close();
|
||||||
|
sheepFilterForm.dataset.state = current === 'open' ? 'closed' : 'open';
|
||||||
|
},
|
||||||
|
close: () => {
|
||||||
|
const sheepFilterForm = document.getElementById("block-sheeps-list-filter");
|
||||||
|
sheepFilterForm.dataset.state = 'closed'
|
||||||
|
},
|
||||||
|
input: (value) => {
|
||||||
|
console.log(value);
|
||||||
|
|
||||||
|
filter_value = value || "";
|
||||||
|
Sheeps.sheeps_list.setHTML({filter: filter_value});
|
||||||
|
}
|
||||||
|
},
|
||||||
search: {
|
search: {
|
||||||
open: () => {
|
open: () => {
|
||||||
const sheepSearchForm = document.getElementById("block-sheeps-list-search");
|
const sheepSearchForm = document.getElementById("block-sheeps-list-search");
|
||||||
sheepSearchForm.classList.toggle('active');
|
|
||||||
const current = sheepSearchForm.dataset.state;
|
const current = sheepSearchForm.dataset.state;
|
||||||
|
|
||||||
|
if(current === 'closed') Sheeps.filter.close();
|
||||||
sheepSearchForm.dataset.state = current === 'open' ? 'closed' : 'open';
|
sheepSearchForm.dataset.state = current === 'open' ? 'closed' : 'open';
|
||||||
},
|
},
|
||||||
|
close: () => {
|
||||||
|
const sheepSearchForm = document.getElementById("block-sheeps-list-search");
|
||||||
|
sheepSearchForm.dataset.state = 'closed'
|
||||||
|
},
|
||||||
input: (value) => {
|
input: (value) => {
|
||||||
console.log(value);
|
console.log(value);
|
||||||
|
|
||||||
search_value = value?.trim()?.toLowerCase() || "";
|
search_value = value?.trim()?.toLowerCase() || "";
|
||||||
Sheeps.sheeps_list.setHTML(search_value);
|
Sheeps.sheeps_list.setHTML({search: search_value});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
territory: {
|
territory: {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
border-radius: calc(var(--border-radius) - 5px);
|
border-radius: calc(var(--border-radius) - 5px);
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
margin-bottom: -10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#block-sheeps-list>.header>h1 {
|
#block-sheeps-list>.header>h1 {
|
||||||
@@ -83,11 +84,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#block-sheeps-list>.filter,
|
||||||
#block-sheeps-list>.search {
|
#block-sheeps-list>.search {
|
||||||
width: calc(100% - 30px);
|
width: calc(100% - 30px);
|
||||||
background-color: var(--PrimaryColor);
|
background-color: var(--PrimaryColor);
|
||||||
border-radius: 0px 0px 10px 10px;
|
border-radius: 10px;
|
||||||
margin: -12px 10px 20px 10px;
|
margin: -2px 10px 0px 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -102,10 +104,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#block-sheeps-list>.filter[data-state="open"],
|
||||||
#block-sheeps-list>.search[data-state="open"] {
|
#block-sheeps-list>.search[data-state="open"] {
|
||||||
max-height: 60px;
|
max-height: 60px;
|
||||||
padding: 22px 5px 5px 5px;
|
padding: 22px 5px 5px 5px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
border-radius: 0px 0px 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#block-sheeps-list>.filter>select{
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: calc(var(--border-radius) - 5px - 4px);
|
||||||
|
height: 30px;
|
||||||
|
background-color: var(--ColorThemes2);
|
||||||
|
color: var(--ColorThemes3);
|
||||||
|
font-size: var(--FontSize2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#block-sheeps-list>.search>input {
|
#block-sheeps-list>.search>input {
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
.page-territory>.list-controls>button {
|
.page-territory>.list-controls>button {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: var(--ColorThemes3);
|
background: var(--PrimaryColor);
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
min-width: 30px;
|
min-width: 30px;
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
.page-territory>.list-controls>button>svg {
|
.page-territory>.list-controls>button>svg {
|
||||||
width: 25px;
|
width: 25px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
fill: var(--ColorThemes0);
|
fill: var(--PrimaryColorText);
|
||||||
}
|
}
|
||||||
|
|
||||||
#page-territory-search {
|
#page-territory-search {
|
||||||
|
|||||||
Reference in New Issue
Block a user