feat(portal): refactor navigation from drawer to sidebar layout
Replace collapsible <details> drawers with a persistent left sidebar for 報表類, 查詢類, and 開發工具 categories. Unify dev tools handling via data-tool-src attribute instead of onclick openTool(). Also release tmtt-defect page status from dev to released. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -53,7 +53,7 @@
|
|||||||
{
|
{
|
||||||
"route": "/tmtt-defect",
|
"route": "/tmtt-defect",
|
||||||
"name": "TMTT印字腳型不良分析",
|
"name": "TMTT印字腳型不良分析",
|
||||||
"status": "dev"
|
"status": "released"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"api_public": true,
|
"api_public": true,
|
||||||
@@ -63,4 +63,4 @@
|
|||||||
"object_count": 19,
|
"object_count": 19,
|
||||||
"source": "tools/query_table_schema.py"
|
"source": "tools/query_table_schema.py"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
import './portal.css';
|
import './portal.css';
|
||||||
|
|
||||||
(function initPortal() {
|
(function initPortal() {
|
||||||
const tabs = document.querySelectorAll('.tab');
|
const sidebarItems = document.querySelectorAll('.sidebar-item[data-target]');
|
||||||
const frames = document.querySelectorAll('iframe');
|
const frames = document.querySelectorAll('iframe');
|
||||||
const toolFrame = document.getElementById('toolFrame');
|
|
||||||
const healthDot = document.getElementById('healthDot');
|
const healthDot = document.getElementById('healthDot');
|
||||||
const healthLabel = document.getElementById('healthLabel');
|
const healthLabel = document.getElementById('healthLabel');
|
||||||
const healthPopup = document.getElementById('healthPopup');
|
const healthPopup = document.getElementById('healthPopup');
|
||||||
const healthStatus = document.getElementById('healthStatus');
|
|
||||||
const dbStatus = document.getElementById('dbStatus');
|
const dbStatus = document.getElementById('dbStatus');
|
||||||
const redisStatus = document.getElementById('redisStatus');
|
const redisStatus = document.getElementById('redisStatus');
|
||||||
const cacheEnabled = document.getElementById('cacheEnabled');
|
const cacheEnabled = document.getElementById('cacheEnabled');
|
||||||
@@ -22,41 +20,37 @@ import './portal.css';
|
|||||||
|
|
||||||
function setFrameHeight() {
|
function setFrameHeight() {
|
||||||
const header = document.querySelector('.header');
|
const header = document.querySelector('.header');
|
||||||
const tabArea = document.querySelector('.tabs');
|
if (!header) return;
|
||||||
if (!header || !tabArea) return;
|
const height = Math.max(600, window.innerHeight - header.offsetHeight - 52);
|
||||||
const height = Math.max(600, window.innerHeight - header.offsetHeight - tabArea.offsetHeight - 60);
|
|
||||||
frames.forEach((frame) => {
|
frames.forEach((frame) => {
|
||||||
frame.style.height = `${height}px`;
|
frame.style.height = `${height}px`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function activateTab(targetId) {
|
function activateTab(targetId, toolSrc) {
|
||||||
tabs.forEach((tab) => tab.classList.remove('active'));
|
sidebarItems.forEach((item) => item.classList.remove('active'));
|
||||||
frames.forEach((frame) => frame.classList.remove('active'));
|
frames.forEach((frame) => frame.classList.remove('active'));
|
||||||
|
|
||||||
const tabBtn = document.querySelector(`[data-target="${targetId}"]`);
|
const activeItems = document.querySelectorAll(`.sidebar-item[data-target="${targetId}"]`);
|
||||||
const targetFrame = document.getElementById(targetId);
|
activeItems.forEach((item) => {
|
||||||
|
if (!toolSrc || item.dataset.toolSrc === toolSrc) {
|
||||||
|
item.classList.add('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (tabBtn) tabBtn.classList.add('active');
|
const targetFrame = document.getElementById(targetId);
|
||||||
if (targetFrame) {
|
if (targetFrame) {
|
||||||
targetFrame.classList.add('active');
|
targetFrame.classList.add('active');
|
||||||
if (targetFrame.dataset.src && !targetFrame.src) {
|
if (toolSrc) {
|
||||||
|
if (targetFrame.src !== toolSrc && !targetFrame.src.endsWith(toolSrc)) {
|
||||||
|
targetFrame.src = toolSrc;
|
||||||
|
}
|
||||||
|
} else if (targetFrame.dataset.src && !targetFrame.src) {
|
||||||
targetFrame.src = targetFrame.dataset.src;
|
targetFrame.src = targetFrame.dataset.src;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openTool(path) {
|
|
||||||
if (!toolFrame) return false;
|
|
||||||
tabs.forEach((tab) => tab.classList.remove('active'));
|
|
||||||
frames.forEach((frame) => frame.classList.remove('active'));
|
|
||||||
toolFrame.classList.add('active');
|
|
||||||
if (toolFrame.src !== path) {
|
|
||||||
toolFrame.src = path;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleHealthPopup() {
|
function toggleHealthPopup() {
|
||||||
if (!healthPopup) return;
|
if (!healthPopup) return;
|
||||||
healthPopup.classList.toggle('show');
|
healthPopup.classList.toggle('show');
|
||||||
@@ -167,18 +161,17 @@ import './portal.css';
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tabs.forEach((tab) => {
|
sidebarItems.forEach((item) => {
|
||||||
tab.addEventListener('click', () => activateTab(tab.dataset.target));
|
item.addEventListener('click', () => {
|
||||||
|
activateTab(item.dataset.target, item.dataset.toolSrc || null);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (tabs.length > 0) {
|
if (sidebarItems.length > 0) {
|
||||||
activateTab(tabs[0].dataset.target);
|
activateTab(sidebarItems[0].dataset.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.openTool = openTool;
|
|
||||||
window.toggleHealthPopup = toggleHealthPopup;
|
window.toggleHealthPopup = toggleHealthPopup;
|
||||||
// Click handler is wired via inline onclick in template for fallback compatibility.
|
|
||||||
// Avoid duplicate binding here, otherwise a single click toggles twice.
|
|
||||||
document.addEventListener('click', (e) => {
|
document.addEventListener('click', (e) => {
|
||||||
if (!e.target.closest('#healthStatus') && !e.target.closest('#healthPopup') && healthPopup) {
|
if (!e.target.closest('#healthStatus') && !e.target.closest('#healthPopup') && healthPopup) {
|
||||||
healthPopup.classList.remove('show');
|
healthPopup.classList.remove('show');
|
||||||
|
|||||||
@@ -1,29 +1,55 @@
|
|||||||
.drawer {
|
/* Sidebar Navigation */
|
||||||
|
.sidebar {
|
||||||
|
width: 200px;
|
||||||
|
min-width: 200px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
border: 1px solid #e3e8f2;
|
border: 1px solid #e3e8f2;
|
||||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
|
||||||
overflow: hidden;
|
overflow-y: auto;
|
||||||
|
align-self: flex-start;
|
||||||
|
position: sticky;
|
||||||
|
top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.drawer > summary {
|
.sidebar-group-title {
|
||||||
list-style: none;
|
padding: 10px 14px 6px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #94a3b8;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-group-title:not(:first-child) {
|
||||||
|
border-top: 1px solid #e2e8f0;
|
||||||
|
margin-top: 4px;
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-item {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
text-align: left;
|
||||||
|
padding: 8px 14px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #475569;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 10px 14px;
|
transition: all 0.15s ease;
|
||||||
font-size: 14px;
|
text-decoration: none;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-item:hover {
|
||||||
|
background: #f1f5f9;
|
||||||
|
color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-item.active {
|
||||||
|
background: #eef2ff;
|
||||||
|
color: #667eea;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #334155;
|
border-right: 3px solid #667eea;
|
||||||
background: #f8fafc;
|
|
||||||
border-bottom: 1px solid #e2e8f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer > summary::-webkit-details-marker {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer-content {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 12px;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -214,86 +214,75 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs {
|
/* Sidebar + Panel Layout */
|
||||||
margin-bottom: 12px;
|
.main-layout {
|
||||||
display: grid;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 12px;
|
||||||
|
min-height: calc(100vh - 140px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.drawer {
|
.sidebar {
|
||||||
|
width: 200px;
|
||||||
|
min-width: 200px;
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
border: 1px solid #e3e8f2;
|
border: 1px solid #e3e8f2;
|
||||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
|
||||||
overflow: hidden;
|
overflow-y: auto;
|
||||||
|
align-self: flex-start;
|
||||||
|
position: sticky;
|
||||||
|
top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.drawer > summary {
|
.sidebar-group-title {
|
||||||
list-style: none;
|
padding: 10px 14px 6px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #94a3b8;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-group-title:not(:first-child) {
|
||||||
|
border-top: 1px solid #e2e8f0;
|
||||||
|
margin-top: 4px;
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-item {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
text-align: left;
|
||||||
|
padding: 8px 14px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #475569;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 10px 14px;
|
transition: all 0.15s ease;
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #334155;
|
|
||||||
background: #f8fafc;
|
|
||||||
border-bottom: 1px solid #e2e8f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer > summary::-webkit-details-marker {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer-content {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab {
|
|
||||||
border: 2px solid #d9d9d9;
|
|
||||||
background: white;
|
|
||||||
color: #333;
|
|
||||||
padding: 10px 18px;
|
|
||||||
border-radius: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab:hover {
|
|
||||||
border-color: #667eea;
|
|
||||||
color: #667eea;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab.active {
|
|
||||||
background: #667eea;
|
|
||||||
border-color: #667eea;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer-link {
|
|
||||||
border: 1px solid #d9d9d9;
|
|
||||||
background: white;
|
|
||||||
color: #333;
|
|
||||||
padding: 10px 18px;
|
|
||||||
border-radius: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: all 0.2s ease;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.drawer-link:hover {
|
.sidebar-item:hover {
|
||||||
border-color: #667eea;
|
background: #f1f5f9;
|
||||||
color: #667eea;
|
color: #667eea;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-item.active {
|
||||||
|
background: #eef2ff;
|
||||||
|
color: #667eea;
|
||||||
|
font-weight: 600;
|
||||||
|
border-right: 3px solid #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
.panel {
|
.panel {
|
||||||
|
flex: 1;
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
|
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel iframe {
|
.panel iframe {
|
||||||
@@ -363,85 +352,74 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tabs">
|
<div class="main-layout">
|
||||||
<details class="drawer" open>
|
<nav class="sidebar">
|
||||||
<summary>報表類</summary>
|
<div class="sidebar-group-title">報表類</div>
|
||||||
<div class="drawer-content">
|
{% if can_view_page('/wip-overview') %}
|
||||||
{% if can_view_page('/wip-overview') %}
|
<button class="sidebar-item" data-target="wipOverviewFrame">WIP 即時概況</button>
|
||||||
<button class="tab" data-target="wipOverviewFrame">WIP 即時概況</button>
|
{% endif %}
|
||||||
{% endif %}
|
{% if can_view_page('/resource') %}
|
||||||
{% if can_view_page('/resource') %}
|
<button class="sidebar-item" data-target="resourceFrame">設備即時概況</button>
|
||||||
<button class="tab" data-target="resourceFrame">設備即時概況</button>
|
{% endif %}
|
||||||
{% endif %}
|
{% if can_view_page('/resource-history') %}
|
||||||
{% if can_view_page('/resource-history') %}
|
<button class="sidebar-item" data-target="resourceHistoryFrame">設備歷史績效</button>
|
||||||
<button class="tab" data-target="resourceHistoryFrame">設備歷史績效</button>
|
{% endif %}
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details class="drawer" open>
|
<div class="sidebar-group-title">查詢類</div>
|
||||||
<summary>查詢類</summary>
|
{% if can_view_page('/tables') %}
|
||||||
<div class="drawer-content">
|
<button class="sidebar-item" data-target="tableFrame">數據表查詢工具</button>
|
||||||
{% if can_view_page('/tables') %}
|
{% endif %}
|
||||||
<button class="tab" data-target="tableFrame">數據表查詢工具</button>
|
{% if can_view_page('/excel-query') %}
|
||||||
{% endif %}
|
<button class="sidebar-item" data-target="excelQueryFrame">Excel 批次查詢</button>
|
||||||
{% if can_view_page('/excel-query') %}
|
{% endif %}
|
||||||
<button class="tab" data-target="excelQueryFrame">Excel 批次查詢</button>
|
{% if can_view_page('/job-query') %}
|
||||||
{% endif %}
|
<button class="sidebar-item" data-target="jobQueryFrame">設備維修查詢</button>
|
||||||
{% if can_view_page('/job-query') %}
|
{% endif %}
|
||||||
<button class="tab" data-target="jobQueryFrame">設備維修查詢</button>
|
{% if can_view_page('/query-tool') %}
|
||||||
{% endif %}
|
<button class="sidebar-item" data-target="queryToolFrame">批次追蹤工具</button>
|
||||||
{% if can_view_page('/query-tool') %}
|
{% endif %}
|
||||||
<button class="tab" data-target="queryToolFrame">批次追蹤工具</button>
|
{% if can_view_page('/tmtt-defect') %}
|
||||||
{% endif %}
|
<button class="sidebar-item" data-target="tmttDefectFrame">TMTT不良分析</button>
|
||||||
{% if can_view_page('/tmtt-defect') %}
|
{% endif %}
|
||||||
<button class="tab" data-target="tmttDefectFrame">TMTT不良分析</button>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details class="drawer">
|
<div class="sidebar-group-title">開發工具</div>
|
||||||
<summary>開發工具</summary>
|
{% if is_admin %}
|
||||||
<div class="drawer-content">
|
<button class="sidebar-item" data-target="toolFrame" data-tool-src="/admin/pages">頁面管理</button>
|
||||||
{% if is_admin %}
|
<button class="sidebar-item" data-target="toolFrame" data-tool-src="/admin/performance">效能監控</button>
|
||||||
<a class="drawer-link" href="#" onclick="return openTool('/admin/pages')">頁面管理</a>
|
{% else %}
|
||||||
<a class="drawer-link" href="#" onclick="return openTool('/admin/performance')">效能監控</a>
|
<a class="sidebar-item" href="{{ url_for('auth.login') }}">管理員登入</a>
|
||||||
{% else %}
|
{% endif %}
|
||||||
<a class="drawer-link" href="{{ url_for('auth.login') }}">管理員登入</a>
|
</nav>
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<!-- Lazy load: iframes load on tab activation -->
|
{% if can_view_page('/wip-overview') %}
|
||||||
{% if can_view_page('/wip-overview') %}
|
<iframe id="wipOverviewFrame" data-src="/wip-overview" title="WIP 即時概況"></iframe>
|
||||||
<iframe id="wipOverviewFrame" data-src="/wip-overview" title="WIP 即時概況"></iframe>
|
{% endif %}
|
||||||
{% endif %}
|
{% if can_view_page('/resource') %}
|
||||||
{% if can_view_page('/resource') %}
|
<iframe id="resourceFrame" data-src="/resource" title="設備即時概況"></iframe>
|
||||||
<iframe id="resourceFrame" data-src="/resource" title="設備即時概況"></iframe>
|
{% endif %}
|
||||||
{% endif %}
|
{% if can_view_page('/tables') %}
|
||||||
{% if can_view_page('/tables') %}
|
<iframe id="tableFrame" data-src="/tables" title="數據表查詢工具"></iframe>
|
||||||
<iframe id="tableFrame" data-src="/tables" title="數據表查詢工具"></iframe>
|
{% endif %}
|
||||||
{% endif %}
|
{% if can_view_page('/excel-query') %}
|
||||||
{% if can_view_page('/excel-query') %}
|
<iframe id="excelQueryFrame" data-src="/excel-query" title="Excel 批次查詢"></iframe>
|
||||||
<iframe id="excelQueryFrame" data-src="/excel-query" title="Excel 批次查詢"></iframe>
|
{% endif %}
|
||||||
{% endif %}
|
{% if can_view_page('/resource-history') %}
|
||||||
{% if can_view_page('/resource-history') %}
|
<iframe id="resourceHistoryFrame" data-src="/resource-history" title="設備歷史績效"></iframe>
|
||||||
<iframe id="resourceHistoryFrame" data-src="/resource-history" title="設備歷史績效"></iframe>
|
{% endif %}
|
||||||
{% endif %}
|
{% if can_view_page('/job-query') %}
|
||||||
{% if can_view_page('/job-query') %}
|
<iframe id="jobQueryFrame" data-src="/job-query" title="設備維修查詢"></iframe>
|
||||||
<iframe id="jobQueryFrame" data-src="/job-query" title="設備維修查詢"></iframe>
|
{% endif %}
|
||||||
{% endif %}
|
{% if can_view_page('/query-tool') %}
|
||||||
{% if can_view_page('/query-tool') %}
|
<iframe id="queryToolFrame" data-src="/query-tool" title="批次追蹤工具"></iframe>
|
||||||
<iframe id="queryToolFrame" data-src="/query-tool" title="批次追蹤工具"></iframe>
|
{% endif %}
|
||||||
{% endif %}
|
{% if can_view_page('/tmtt-defect') %}
|
||||||
{% if can_view_page('/tmtt-defect') %}
|
<iframe id="tmttDefectFrame" data-src="/tmtt-defect" title="TMTT不良分析"></iframe>
|
||||||
<iframe id="tmttDefectFrame" data-src="/tmtt-defect" title="TMTT不良分析"></iframe>
|
{% endif %}
|
||||||
{% endif %}
|
{% if is_admin %}
|
||||||
{% if is_admin %}
|
<iframe id="toolFrame" title="開發工具"></iframe>
|
||||||
<iframe id="toolFrame" title="開發工具"></iframe>
|
{% endif %}
|
||||||
{% endif %}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -452,61 +430,53 @@
|
|||||||
<script type="module" src="{{ portal_js }}"></script>
|
<script type="module" src="{{ portal_js }}"></script>
|
||||||
{% else %}
|
{% else %}
|
||||||
<script>
|
<script>
|
||||||
const tabs = document.querySelectorAll('.tab');
|
const sidebarItems = document.querySelectorAll('.sidebar-item[data-target]');
|
||||||
const frames = document.querySelectorAll('iframe');
|
const frames = document.querySelectorAll('iframe');
|
||||||
const toolFrame = document.getElementById('toolFrame');
|
const toolFrame = document.getElementById('toolFrame');
|
||||||
|
|
||||||
function setFrameHeight() {
|
function setFrameHeight() {
|
||||||
const headerHeight = document.querySelector('.header').offsetHeight;
|
const headerHeight = document.querySelector('.header').offsetHeight;
|
||||||
const tabsHeight = document.querySelector('.tabs').offsetHeight;
|
const padding = 52;
|
||||||
const padding = 60;
|
const height = Math.max(600, window.innerHeight - headerHeight - padding);
|
||||||
const height = Math.max(600, window.innerHeight - headerHeight - tabsHeight - padding);
|
|
||||||
frames.forEach(frame => {
|
frames.forEach(frame => {
|
||||||
frame.style.height = `${height}px`;
|
frame.style.height = `${height}px`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function activateTab(targetId) {
|
function activateTab(targetId, toolSrc) {
|
||||||
tabs.forEach(tab => tab.classList.remove('active'));
|
sidebarItems.forEach(item => item.classList.remove('active'));
|
||||||
frames.forEach(frame => frame.classList.remove('active'));
|
frames.forEach(frame => frame.classList.remove('active'));
|
||||||
|
|
||||||
const tabBtn = document.querySelector(`[data-target="${targetId}"]`);
|
const activeItems = document.querySelectorAll(`.sidebar-item[data-target="${targetId}"]`);
|
||||||
const targetFrame = document.getElementById(targetId);
|
activeItems.forEach(item => {
|
||||||
|
if (!toolSrc || item.dataset.toolSrc === toolSrc) {
|
||||||
|
item.classList.add('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (tabBtn) tabBtn.classList.add('active');
|
const targetFrame = document.getElementById(targetId);
|
||||||
if (targetFrame) {
|
if (targetFrame) {
|
||||||
targetFrame.classList.add('active');
|
targetFrame.classList.add('active');
|
||||||
// Lazy load: load iframe src on first activation
|
if (toolSrc) {
|
||||||
if (targetFrame.dataset.src && !targetFrame.src) {
|
// Dev tools: load the specific tool URL
|
||||||
|
if (targetFrame.src !== toolSrc && !targetFrame.src.endsWith(toolSrc)) {
|
||||||
|
targetFrame.src = toolSrc;
|
||||||
|
}
|
||||||
|
} else if (targetFrame.dataset.src && !targetFrame.src) {
|
||||||
targetFrame.src = targetFrame.dataset.src;
|
targetFrame.src = targetFrame.dataset.src;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openTool(path) {
|
sidebarItems.forEach(item => {
|
||||||
if (!toolFrame) return false;
|
item.addEventListener('click', () => {
|
||||||
tabs.forEach(tab => tab.classList.remove('active'));
|
activateTab(item.dataset.target, item.dataset.toolSrc || null);
|
||||||
frames.forEach(frame => frame.classList.remove('active'));
|
});
|
||||||
toolFrame.classList.add('active');
|
|
||||||
if (toolFrame.src !== path) {
|
|
||||||
toolFrame.src = path;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
window.openTool = openTool;
|
|
||||||
|
|
||||||
tabs.forEach(tab => {
|
|
||||||
tab.addEventListener('click', () => activateTab(tab.dataset.target));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Auto-activate first available tab
|
// Auto-activate first available item
|
||||||
if (tabs.length > 0) {
|
if (sidebarItems.length > 0) {
|
||||||
const firstTab = tabs[0];
|
activateTab(sidebarItems[0].dataset.target);
|
||||||
// Clear any pre-set active states
|
|
||||||
tabs.forEach(tab => tab.classList.remove('active'));
|
|
||||||
frames.forEach(frame => frame.classList.remove('active'));
|
|
||||||
// Activate first tab
|
|
||||||
activateTab(firstTab.dataset.target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('resize', setFrameHeight);
|
window.addEventListener('resize', setFrameHeight);
|
||||||
|
|||||||
Reference in New Issue
Block a user