From 8023319ee482d76e691b675a7d1a7288035cd89a Mon Sep 17 00:00:00 2001 From: poignatov Date: Wed, 4 Feb 2026 14:35:47 +0300 Subject: [PATCH] =?UTF-8?q?4.13.6:=20=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=B0=D1=80=D1=85=D0=B8=D1=82?= =?UTF-8?q?=D0=B5=D0=BA=D1=82=D1=83=D1=80=D1=8B=20=D1=82=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION | 2 +- play-life-web/package.json | 2 +- play-life-web/src/App.jsx | 214 +++++++++++++++++++------------------ 3 files changed, 112 insertions(+), 106 deletions(-) diff --git a/VERSION b/VERSION index e0d9923..d03dd2c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.13.5 +4.13.6 diff --git a/play-life-web/package.json b/play-life-web/package.json index 344a672..63376bd 100644 --- a/play-life-web/package.json +++ b/play-life-web/package.json @@ -1,6 +1,6 @@ { "name": "play-life-web", - "version": "4.13.5", + "version": "4.13.6", "type": "module", "scripts": { "dev": "vite", diff --git a/play-life-web/src/App.jsx b/play-life-web/src/App.jsx index f7d1d2f..1681cdb 100644 --- a/play-life-web/src/App.jsx +++ b/play-life-web/src/App.jsx @@ -130,8 +130,6 @@ function AppContent() { const [wordsRefreshTrigger, setWordsRefreshTrigger] = useState(0) const [wishlistRefreshTrigger, setWishlistRefreshTrigger] = useState(0) - // Состояние для сохранения позиции скролла каждого таба - const [scrollPositions, setScrollPositions] = useState({}) // Восстанавливаем последний выбранный таб после перезагрузки @@ -710,15 +708,6 @@ function AppContent() { setTabParams({}) updateUrl('full', {}, activeTab) } else if (tab !== activeTab || tab === 'task-form' || tab === 'wishlist-form' || (tab === 'words' && Object.keys(params).length > 0)) { - // Сохраняем позицию скролла для текущего таба перед переходом - const scrollContainer = document.querySelector('.flex-1.overflow-y-auto') - if (scrollContainer && mainTabs.includes(activeTab)) { - setScrollPositions(prev => ({ - ...prev, - [activeTab]: scrollContainer.scrollTop - })) - } - // Для task-form и wishlist-form всегда обновляем параметры, даже если это тот же таб markTabAsLoaded(tab) @@ -865,20 +854,6 @@ function AppContent() { } }, [selectedProject, activeTab]) - // Восстанавливаем позицию скролла при возвращении на таб - useEffect(() => { - if (mainTabs.includes(activeTab) && scrollPositions[activeTab] !== undefined) { - // Используем setTimeout, чтобы DOM успел обновиться - const timeoutId = setTimeout(() => { - const scrollContainer = document.querySelector('.flex-1.overflow-y-auto') - if (scrollContainer) { - scrollContainer.scrollTop = scrollPositions[activeTab] - } - }, 0) - - return () => clearTimeout(timeoutId) - } - }, [activeTab, scrollPositions]) // Определяем общее состояние загрузки и ошибок для кнопки Refresh @@ -897,45 +872,47 @@ function AppContent() { // Определяем, нужно ли скрывать нижнюю панель (для fullscreen экранов) const isFullscreenTab = activeTab === 'test' || activeTab === 'add-words' || activeTab === 'task-form' || activeTab === 'wishlist-form' || activeTab === 'wishlist-detail' || activeTab === 'todoist-integration' || activeTab === 'telegram-integration' || activeTab === 'full' || activeTab === 'priorities' || activeTab === 'words' || activeTab === 'dictionaries' - // Определяем отступы для контейнера - const getContainerPadding = () => { - if (!isFullscreenTab) { - // Для tasks, wishlist и profile на широких экранах увеличиваем отступ - if (activeTab === 'tasks' || activeTab === 'wishlist' || activeTab === 'profile') { - return 'p-4 md:p-8' - } - return 'p-4 md:p-6' + // Функция для получения классов скролл-контейнера для каждого таба + // Каждый таб имеет свой изолированный скролл-контейнер для автоматического сохранения позиции скролла + const getTabContainerClasses = (tabName) => { + const isActive = activeTab === tabName + const baseClasses = 'absolute inset-0 overflow-y-auto' + // Активный таб: z-10 (сверху), неактивные: z-0 + invisible + opacity-0 (мгновенное скрытие) + const visibilityClasses = isActive ? 'z-10' : 'z-0 invisible opacity-0 pointer-events-none' + + // Определяем padding для каждого таба + let paddingClasses = '' + if (tabName === 'current' || tabName === 'tasks' || tabName === 'wishlist' || tabName === 'profile') { + paddingClasses = 'pb-20' + } else if (tabName === 'words' || tabName === 'dictionaries') { + paddingClasses = 'pb-16' } - // Для экрана статистики используем такие же отступы как для приоритетов - if (activeTab === 'full') { - return 'px-4 md:px-8 py-0' + + return `${baseClasses} ${paddingClasses} ${visibilityClasses}`.trim() + } + + // Функция для определения отступов внутреннего контейнера + const getInnerContainerClasses = (tabName) => { + if (tabName === 'tasks' || tabName === 'wishlist' || tabName === 'profile') { + return 'max-w-7xl mx-auto p-4 md:p-8' } - // Для экрана приоритетов используем такие же отступы как для profile - if (activeTab === 'priorities') { - return 'px-4 md:px-8 py-0' + if (tabName === 'current') { + return 'max-w-7xl mx-auto p-4 md:p-6' } - // Для экрана словарей используем такие же отступы как для приоритетов - if (activeTab === 'dictionaries') { - return 'px-4 md:px-8 py-0' + if (tabName === 'full' || tabName === 'priorities' || tabName === 'dictionaries' || tabName === 'words') { + return 'max-w-7xl mx-auto px-4 md:px-8 py-0' } - // Для экрана списка слов используем такие же отступы как для словарей - if (activeTab === 'words') { - return 'px-4 md:px-8 py-0' - } - // Для экрана желаний используем такие же отступы как для словарей - if (activeTab === 'wishlist') { - return 'px-4 md:px-8 py-0' - } - // Для остальных fullscreen экранов без отступов - return 'p-0' + // Fullscreen табы без отступов + return 'max-w-7xl mx-auto p-0' } return (
-
-
- {loadedTabs.current && ( -
+ {/* Контейнер табов - каждый таб имеет свой изолированный скролл */} +
+ {loadedTabs.current && ( +
+
- )} +
+ )} - {loadedTabs.priorities && ( -
+ {loadedTabs.priorities && ( +
+
- )} +
+ )} - {loadedTabs.full && ( -
+ {loadedTabs.full && ( +
+
{ @@ -985,10 +966,12 @@ function AppContent() { activeTab={activeTab} />
- )} +
+ )} - {loadedTabs.words && ( -
+ {loadedTabs.words && ( +
+
- )} +
+ )} - {loadedTabs['add-words'] && ( -
+ {loadedTabs['add-words'] && ( +
+
- )} +
+ )} - {loadedTabs.dictionaries && ( -
+ {loadedTabs.dictionaries && ( +
+
- )} +
+ )} - {loadedTabs.test && ( -
+ {loadedTabs.test && ( +
+
- )} +
+ )} - {loadedTabs.tasks && ( -
+ {loadedTabs.tasks && ( +
+
fetchTasksData(isBackground)} />
- )} +
+ )} - {loadedTabs['task-form'] && ( -
+ {loadedTabs['task-form'] && ( +
+
- )} +
+ )} - {loadedTabs.wishlist && ( -
+ {loadedTabs.wishlist && ( +
+
- )} +
+ )} - {loadedTabs['wishlist-form'] && ( -
+ {loadedTabs['wishlist-form'] && ( +
+
- )} +
+ )} - - {loadedTabs['board-form'] && ( -
+ {loadedTabs['board-form'] && ( +
+
setWishlistRefreshTrigger(prev => prev + 1)} />
- )} +
+ )} - {loadedTabs['board-join'] && ( -
+ {loadedTabs['board-join'] && ( +
+
- )} +
+ )} - {loadedTabs.profile && ( -
+ {loadedTabs.profile && ( +
+
- )} +
+ )} - {loadedTabs['todoist-integration'] && ( -
+ {loadedTabs['todoist-integration'] && ( +
+
- )} +
+ )} - {loadedTabs['telegram-integration'] && ( -
+ {loadedTabs['telegram-integration'] && ( +
+
- )} -
+
+ )}
{/* Кнопка добавления задачи (только для таба задач) */}