From 2056a43cced4a5d67f9e58226300a850768d4beb Mon Sep 17 00:00:00 2001 From: Ali <010109ali@gmail.com> Date: Mon, 21 Jul 2025 12:28:31 +0400 Subject: [PATCH] new branch for frappe v16 --- custom_subtabs/public/css/custom_subtabs.css | 79 ++++++++- custom_subtabs/public/js/custom_subtabs.js | 175 ++++++++++--------- 2 files changed, 168 insertions(+), 86 deletions(-) diff --git a/custom_subtabs/public/css/custom_subtabs.css b/custom_subtabs/public/css/custom_subtabs.css index d55088f..8394b2e 100644 --- a/custom_subtabs/public/css/custom_subtabs.css +++ b/custom_subtabs/public/css/custom_subtabs.css @@ -1,18 +1,25 @@ +/* Основной контейнер субтабов */ .sub-tab-container { margin: 0 !important; padding: 0 !important; display: flex !important; justify-content: flex-start !important; - border-bottom: 1px solid #ddd !important; + border-bottom: 1px solid #dee2e6 !important; + background-color: #fff !important; + position: relative !important; + z-index: 1 !important; } +/* Отдельные субтабы */ .sub-tab-container .sub-tab { margin: 0 !important; background: none !important; border: none !important; padding: 0 !important; + position: relative !important; } +/* Ссылки в субтабах */ .sub-tab-container .sub-tab a { text-decoration: none !important; display: block !important; @@ -24,16 +31,74 @@ margin: 0 !important; background: transparent !important; box-shadow: none !important; + border-radius: 0 !important; + transition: color 0.15s ease-in-out !important; + position: relative !important; } .sub-tab-container .sub-tab a:hover { - color: #000 !important; /* Цвет текста при наведении */ + color: #000 !important; /* Только изменение цвета, без фона */ } +/* Активный субтаб */ .sub-tab-container .sub-tab.active a { - color: #000 !important; /* Черный текст для активного таба */ - font-weight: 600 !important; /* Жирный шрифт */ - border-bottom: 1px solid #000 !important; /* Черная активная полоска снизу */ - background-color: transparent !important; /* Без фона */ - border-radius: 0 !important; /* Без скруглений */ + color: #000 !important; + font-weight: 600 !important; + border-bottom: 2px solid #000 !important; + background-color: transparent !important; + border-radius: 0 !important; + margin-bottom: -1px !important; /* Перекрываем нижнюю границу контейнера */ } + +/* Интеграция с основными табами ERPNext */ +.form-tabs + .sub-tab-container { + border-top: none !important; + margin-top: -1px !important; +} + +/* Убираем лишние отступы для форм с субтабами */ +.sub-tab-container + .form-layout, +.sub-tab-container ~ .form-layout { + margin-top: 0 !important; +} + +/* Адаптивность */ +@media (max-width: 768px) { + .sub-tab-container { + flex-wrap: wrap !important; + } + + .sub-tab-container .sub-tab a { + padding: 8px 12px !important; + font-size: 13px !important; + } +} + +/* Убираем конфликтующие стили Bootstrap */ +.sub-tab-container .nav-link { + padding: 0 !important; + border: none !important; + background: none !important; + margin: 0 !important; + border-radius: 0 !important; +} + +/* Плавное появление/исчезание */ +.sub-tab-container { + opacity: 1 !important; + transition: opacity 0.2s ease-in-out !important; +} + +.sub-tab-container[style*="display: none"] { + opacity: 0 !important; +} + +/* Убираем лишние границы и тени */ +.sub-tab-container .sub-tab, +.sub-tab-container .sub-tab a, +.sub-tab-container .nav-item { + box-shadow: none !important; + border-left: none !important; + border-right: none !important; + border-top: none !important; +} \ No newline at end of file diff --git a/custom_subtabs/public/js/custom_subtabs.js b/custom_subtabs/public/js/custom_subtabs.js index 84e0a70..a806191 100644 --- a/custom_subtabs/public/js/custom_subtabs.js +++ b/custom_subtabs/public/js/custom_subtabs.js @@ -1,11 +1,7 @@ frappe.ui.form.Layout.prototype.setup_tab_events = function () { - setTimeout(() => { const tabs = this.wrapper.find("ul.form-tabs > li.nav-item"); - - // Получение текущего Doctype в нижнем регистре const currentDoctype = cur_frm.doctype; - const hierarchy = frappe.boot.tab_hierarchy[currentDoctype]; if (!hierarchy) { @@ -30,49 +26,6 @@ frappe.ui.form.Layout.prototype.setup_tab_events = function () { }); } - // Function to handle showing/hiding subtabs and sub-subtabs - function findParentAndShowSubtabs(currentTabName) { - if (!currentTabName) return; - - let parentTabName = null; - - // Find the parent tab and its subtabs - for (const [mainTab, subtabsArray] of Object.entries(subtabs)) { - if (subtabsArray.includes(currentTabName)) { - parentTabName = mainTab; - break; - } - } - - if (!parentTabName) { - return; - } - - // Show the subtabs for the parent tab - const parentSubtabs = subtabs[parentTabName] || []; - parentSubtabs.forEach((subtabName) => { - const subtabElement = $(`.nav-item:contains(${subtabName})`); - const subtabContentId = `${currentDoctype.toLowerCase().replace(/\s+/g, '-')}-${subtabName.toLowerCase().replace(/\s+/g, '_')}_tab`; - const subtabContent = $(`#${subtabContentId}`); - - if (subtabElement.length) { - subtabElement.css("display", "block"); - - // If the current tab is the subtab, show its content - if (subtabName === currentTabName) { - subtabContent.addClass("active show").css("display", "block"); - } - } - }); - - // Show the parent tab's content - const parentTabContentId = `${currentDoctype.toLowerCase().replace(/\s+/g, '-')}-${parentTabName.toLowerCase().replace(/\s+/g, '_')}_tab`; - showTabContent(parentTabContentId); - - // Recursively handle the parent tab - findParentAndShowSubtabs(parentTabName); - } - // Process subtabs tabs.each((index, tab) => { const tabLabel = $(tab).text().trim(); @@ -85,20 +38,44 @@ frappe.ui.form.Layout.prototype.setup_tab_events = function () { return; } - const sectionContainer = tabContent.find(".row.form-section.card-section.visible-section"); + const possibleSelectors = [ + ".row.form-section.card-section.visible-section", + ".form-section", + ".row.form-section", + ".card-section", + ".visible-section", + ".row", + ".section", + "div[class*='section']", + "div[class*='form']" + ]; + + let sectionContainer = null; + + possibleSelectors.forEach(selector => { + const found = tabContent.find(selector); + if (found.length > 0 && !sectionContainer) { + sectionContainer = found.first(); + } + }); - if (!sectionContainer.length) { - return; + if (!sectionContainer || sectionContainer.length === 0) { + sectionContainer = tabContent; } - let subtabContainer = sectionContainer.find('.sub-tab-container'); + // Ищем контейнер субтабов рядом с основными табами, а не внутри контента + const mainTabsContainer = this.wrapper.find("ul.form-tabs"); + let subtabContainer = mainTabsContainer.siblings('.sub-tab-container[data-parent-id="' + tabContentId + '"]'); + if (!subtabContainer.length) { - subtabContainer = $("
"); - sectionContainer.append(subtabContainer); + // Создаем контейнер субтабов после основных табов + subtabContainer = $(""); + mainTabsContainer.after(subtabContainer); } subtabs[tabLabel].forEach((subtab) => { const subtabElement = tabs.filter((_, el) => $(el).text().trim() === subtab); + if (subtabElement.length) { const subtabClone = subtabElement.clone(); subtabClone.addClass("sub-tab"); @@ -112,8 +89,8 @@ frappe.ui.form.Layout.prototype.setup_tab_events = function () { } }); - // Handle main tab activation - $(".nav-item").on("click", function () { + // Handle main tab activation (исключаем субтабы) + $(".nav-item:not(.sub-tab)").off("click.subtabs").on("click.subtabs", function () { const clickedTab = $(this); const tabId = clickedTab.find("a").attr("aria-controls"); @@ -127,51 +104,91 @@ frappe.ui.form.Layout.prototype.setup_tab_events = function () { $(".nav-item").removeClass("active"); clickedTab.addClass("active"); + // Скрываем все контейнеры субтабов + $(".sub-tab-container").css("display", "none"); + + // Показываем контейнер субтабов для активного таба $(".sub-tab-container").each(function () { const containerParentId = $(this).attr("data-parent-id"); + if (containerParentId === tabId) { - $(this).find(".sub-tab").css("display", "block"); - } else { - $(this).find(".sub-tab").css("display", "none"); + $(this).css("display", "flex"); + $(this).find(".sub-tab").removeClass("active"); } }); }); - // Handle subtab activation - $(".sub-tab").on("click", function (event) { + // Handle subtab activation (упрощенная логика) + $(document).off("click.subtabs", ".sub-tab").on("click.subtabs", ".sub-tab", function (event) { event.stopPropagation(); const clickedSubtab = $(this); const subtabId = clickedSubtab.find("a").attr("aria-controls"); - const subtabName = clickedSubtab.text().trim(); + const parentTabId = clickedSubtab.attr("data-parent-id"); - $(".sub-tab").removeClass("active"); + // Делаем активным только кликнутый субтаб в этом контейнере + clickedSubtab.siblings(".sub-tab").removeClass("active"); clickedSubtab.addClass("active"); + // Скрываем все контенты табов hideAllTabContents(); + // Показываем контент субтаба const subtabContent = $(`#${subtabId}`); + if (subtabContent.length) { subtabContent.addClass("active show").css("display", "block"); } - findParentAndShowSubtabs(subtabName); + // Оставляем родительский таб активным и видимым + $(".nav-item:not(.sub-tab)").removeClass("active"); + const parentTab = $(`.nav-item:not(.sub-tab) a[aria-controls="${parentTabId}"]`).closest('.nav-item'); + if (parentTab.length) { + parentTab.addClass("active"); + } + + // Убеждаемся что контейнер субтабов остается видимым + const subtabContainer = clickedSubtab.closest('.sub-tab-container'); + if (subtabContainer.length) { + subtabContainer.css("display", "flex"); + } }); - // Debug log - setInterval(() => { - const activeMainTab = $(".nav-item.active").text().trim(); - const activeSubTabs = $(".sub-tab.active") - .map(function () { - return $(this).text().trim(); - }) - .get(); - const visibleContent = $(".form-tab-content > .tab-pane:visible") - .map(function () { - return $(this).attr("id"); - }) - .get(); - - }, 2000); + // Проверяем активную вкладку при загрузке страницы + let activeTab = $(".nav-item:not(.sub-tab).active"); + + // Способ 2: Поиск по активному контенту + if (!activeTab.length) { + const activeContent = $(".form-tab-content > .tab-pane.active"); + + if (activeContent.length) { + const activeContentId = activeContent.attr('id'); + activeTab = $(`.nav-item a[aria-controls="${activeContentId}"]`).closest('.nav-item'); + } + } + + // Способ 3: Поиск по показанному контенту + if (!activeTab.length) { + const visibleContent = $(".form-tab-content > .tab-pane:visible"); + + if (visibleContent.length) { + const visibleContentId = visibleContent.first().attr('id'); + activeTab = $(`.nav-item a[aria-controls="${visibleContentId}"]`).closest('.nav-item'); + } + } + + if (activeTab.length) { + const activeTabId = activeTab.find("a").attr("aria-controls"); + + // Проверяем есть ли субтабы у активной вкладки + $(".sub-tab-container").each(function () { + const containerParentId = $(this).attr("data-parent-id"); + + if (containerParentId === activeTabId) { + $(this).css("display", "flex"); + $(this).find(".sub-tab").removeClass("active"); + } + }); + } }, 500); -}; +}; \ No newline at end of file