From 0d9db942f861c6c1e53eb01ed41e0d33a6dc5a92 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 22 Jul 2026 17:38:48 +0800 Subject: [PATCH] feat(nav): close P0/P1 parity gaps (nav style, history, plate, admin build) Wire header_nav_style transparent/blur, login history sync, plate publish type, clean dead search CSS, rebuild admin SPA locally (build gitignored). --- .../src/schema/sections/page-display/index.js | 2 + assets/css/frontend/nav.css | 3 +- assets/css/frontend/nav/zib-nav-style.css | 39 ++++++++++ assets/css/frontend/nav/zib-search-box.css | 3 +- assets/css/frontend/nav/zib-utilities.css | 73 ++----------------- assets/js/header-inline-search.js | 57 ++++++++++++++- assets/js/header-newadd.js | 14 +++- header.php | 5 ++ inc/helpers/aksrt-header-menu-buttons.php | 23 ++++++ inc/helpers/header/aksrt-header-render.php | 21 ++++-- src/AKSRT/Admin/Schema/SchemaPageDisplay.php | 2 + tools/nav-zibll-acceptance.md | 11 +++ tools/nav-zibll-contract-report.txt | 13 ++-- 13 files changed, 181 insertions(+), 85 deletions(-) create mode 100644 assets/css/frontend/nav/zib-nav-style.css diff --git a/assets/admin/src/schema/sections/page-display/index.js b/assets/admin/src/schema/sections/page-display/index.js index a9786a1b..d8a4b7f6 100644 --- a/assets/admin/src/schema/sections/page-display/index.js +++ b/assets/admin/src/schema/sections/page-display/index.js @@ -138,11 +138,13 @@ export const pageDisplaySchemas = { { label: '文章', value: 'post' }, { label: '帖子', value: 'topic' }, { label: '商品', value: 'product' }, + { label: '版块', value: 'plate' }, ], dependsOn: { key: 'header_newposts_enabled', value: true }, help: '未开启的模块不会显示' }, { key: 'header_user_newposts_btn', label: '用户卡发布类型', type: 'select', default: 'post', options: [ { label: '文章', value: 'post' }, { label: '帖子', value: 'topic' }, { label: '商品', value: 'product' }, + { label: '版块', value: 'plate' }, ] }, { key: 'header_user_newposts_text', label: '用户卡发布文案', type: 'input', default: '发布内容' }, { key: 'header_nav_pay_vip', label: '导航栏开通会员按钮', type: 'switch', default: true, help: '顶部导航显示开通会员' }, diff --git a/assets/css/frontend/nav.css b/assets/css/frontend/nav.css index 97ddde74..c47573f3 100644 --- a/assets/css/frontend/nav.css +++ b/assets/css/frontend/nav.css @@ -4,6 +4,7 @@ @import url("nav/zib-senior-submenu.css"); @import url("nav/zib-utilities.css"); @import url("nav/zib-search-box.css"); +@import url("nav/zib-nav-style.css"); @import url("nav/header-menu-buttons.css"); @import url("nav/header-userbox.css"); -@import url("nav/dark-mode-submenu.css"); +@import url("nav/dark-mode-submenu.css"); \ No newline at end of file diff --git a/assets/css/frontend/nav/zib-nav-style.css b/assets/css/frontend/nav/zib-nav-style.css new file mode 100644 index 00000000..aca51b5f --- /dev/null +++ b/assets/css/frontend/nav/zib-nav-style.css @@ -0,0 +1,39 @@ +/* Header nav style: transparent / blur (header_nav_style) */ + +body.header-nav-style-transparent .header:not(.show-slide) .navbar-top, +body.header-nav-style-transparent .header.nav-style-transparent .navbar-top { + background: transparent !important; + box-shadow: none !important; +} + +body.header-nav-style-blur .header:not(.show-slide) .navbar-top, +body.header-nav-style-blur .header.nav-style-blur .navbar-top, +.header.nav-style-blur .navbar-top { + background: var(--blur-bg, rgba(255, 255, 255, 0.72)) !important; + -webkit-backdrop-filter: saturate(5) blur(16px); + backdrop-filter: saturate(5) blur(16px); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.04); +} + +body.theme-dark.header-nav-style-blur .header:not(.show-slide) .navbar-top, +html.dark body.header-nav-style-blur .header:not(.show-slide) .navbar-top, +body.dark-theme.header-nav-style-blur .header:not(.show-slide) .navbar-top { + background: var(--blur-bg, rgba(20, 22, 28, 0.72)) !important; +} + +/* When show-slide + fixed, keep zibll transparent gradient (already in core). + Transparent style reinforces that paint while resting at top of hero. */ +body.nav-fixed.header-nav-style-transparent:not(.body-scroll):not(.mobile-navbar-show) .header.show-slide, +body.nav-fixed.header-nav-style-blur:not(.body-scroll):not(.mobile-navbar-show) .header.show-slide { + --header-bg: linear-gradient(0, rgba(0, 0, 0, 0) 5%, rgba(0, 0, 0, 0.12) 80%); + --header-color: #fff; +} + +body.header-nav-style-transparent .header.show-slide .navbar-top, +body.header-nav-style-blur .header.show-slide .navbar-top { + background: transparent !important; + -webkit-backdrop-filter: unset; + backdrop-filter: unset; + box-shadow: none !important; + color: var(--header-color, inherit); +} diff --git a/assets/css/frontend/nav/zib-search-box.css b/assets/css/frontend/nav/zib-search-box.css index 65d28747..28fc8f03 100644 --- a/assets/css/frontend/nav/zib-search-box.css +++ b/assets/css/frontend/nav/zib-search-box.css @@ -92,9 +92,8 @@ display: none; } -/* Full navbar-search slide (layout 1/3) */ +/* Full navbar-search slide (layout 1/3) — position/transition from zib-navbar-core */ .navbar-search { - position: fixed; left: 0; right: 0; top: 0; diff --git a/assets/css/frontend/nav/zib-utilities.css b/assets/css/frontend/nav/zib-utilities.css index ae677c6b..7c39a6e2 100644 --- a/assets/css/frontend/nav/zib-utilities.css +++ b/assets/css/frontend/nav/zib-utilities.css @@ -163,71 +163,6 @@ body.mobile-navbar-show { overflow: hidden; } .aksrt-sme-field span { display: block; font-size: 12px; color: #646970; } .aksrt-sme-card, .aksrt-sme-col { border: 1px solid #dcdcde; padding: 8px; margin: 8px 0; border-radius: 4px; } -/* Layout-2 remote menu search panel (zibll menu_search) */ -.aksrt-remote-box[data-aksrt-menu-search-remote] { - min-height: 8px; -} -.navbar-inline-search-panel { - min-width: 300px; - max-width: 380px; - max-height: 70vh; - overflow: auto; -} -.aksrt-menu-search-posts__item { - display: flex; - flex-direction: column; - gap: 2px; - padding: 6px 2px; - color: inherit; - text-decoration: none; - border-bottom: 1px solid rgba(0,0,0,.04); -} -.aksrt-menu-search-posts__item:last-child { border-bottom: 0; } -.aksrt-menu-search-posts__item:hover { color: var(--focus-color); } -.aksrt-menu-search-history__list .but, -.navbar-inline-search-hots .but { margin: 0 4px 4px 0; } -.loading-spot { - display: inline-block; - width: 1em; - height: 1em; - margin: 8px; - border: 2px solid rgba(0,0,0,.15); - border-top-color: var(--focus-color, #f04494); - border-radius: 50%; - animation: aksrt-spin .7s linear infinite; -} -@keyframes aksrt-spin { to { transform: rotate(360deg); } } -.navbar-but .radius, -.navbar-but .but.radius, -.navbar-cta.radius { - border-radius: 999px !important; -} - -/* Active menu underline (zibll-like) */ -.navbar-top .nav > li.current-menu-item > a, -.navbar-top .nav > li.current-menu-ancestor > a { - color: var(--focus-color); -} -.navbar-top .nav > li.current-menu-item > a::before, -.navbar-top .nav > li.current-menu-ancestor > a::before { - content: ""; - position: absolute; - left: 10px; - right: 10px; - top: 0; - height: 3px; - border-radius: 0 0 3px 3px; - background: var(--focus-color); -} -.navbar-top .nav > li > a { position: relative; } - -/* Type chips in menu search */ -.aksrt-menu-search-types .but { margin: 0 4px; } -.aksrt-menu-search-types .but.active { - --this-bg: var(--focus-color-opacity1, rgba(240,68,148,.12)); - --this-color: var(--focus-color); - color: var(--focus-color); -} /* Navbar form vertical align */ .navbar-top .navbar-form { margin: 0; @@ -266,3 +201,11 @@ body.mobile-navbar-show { overflow: hidden; } @media (min-width: 992px) { .navbar-top .show-nav-but { display: none !important; } } + +/* focus-show: reveal sibling dropdown when input focused (zibll) */ +.form-group:focus-within .dropdown-menu.hover-show-con, +.hover-show:focus-within .hover-show-con { + visibility: unset; + opacity: 1; + transform: translateY(0); +} diff --git a/assets/js/header-inline-search.js b/assets/js/header-inline-search.js index d4604713..9effc6d7 100644 --- a/assets/js/header-inline-search.js +++ b/assets/js/header-inline-search.js @@ -41,6 +41,34 @@ } catch (e) {} } + + function saveLoggedHistory(keyword, type) { + if (!(window.aksrtData && aksrtData.isLoggedIn) || !ajaxUrl() || !keyword) return; + var items = readLocalHistory().filter(function (k) { return k !== keyword; }); + items.unshift(keyword); + items = items.slice(0, 20); + // Also update local cache for panel paint consistency. + writeLocalHistory(items); + var payload = + "action=aksrt_search_history&nonce=" + + encodeURIComponent(nonce()) + + "&mode=save&items=" + + encodeURIComponent( + JSON.stringify( + items.map(function (k) { + return { keyword: k, type: type || "post" }; + }) + ) + ); + try { + var xhr = new XMLHttpRequest(); + xhr.open("POST", ajaxUrl(), true); + xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); + xhr.send(payload); + } catch (e) {} + } + + function keywordChips(list) { return (list || []) .map(function (kw) { @@ -232,7 +260,9 @@ form.addEventListener("submit", function () { var q = (input.value || "").trim(); if (!q) return; - if (!(window.aksrtData && aksrtData.isLoggedIn)) { + if (window.aksrtData && aksrtData.isLoggedIn) { + saveLoggedHistory(q, typeValue()); + } else { var list = readLocalHistory().filter(function (k) { return k !== q; }); @@ -297,8 +327,33 @@ }); } + + function bindFullSearchBoxHistory() { + document.querySelectorAll('form[data-aksrt-search-form="box"]').forEach(function (form) { + if (form._aksrtHistBound) return; + form._aksrtHistBound = 1; + form.addEventListener("submit", function () { + var input = form.querySelector('input[name="s"]'); + var q = input ? (input.value || "").trim() : ""; + if (!q) return; + var typeInput = form.querySelector('input[name="aksrt_search_type"], input[name="type"]'); + var type = (typeInput && typeInput.value) || "post"; + if (window.aksrtData && aksrtData.isLoggedIn) { + saveLoggedHistory(q, type); + } else { + var list = readLocalHistory().filter(function (k) { + return k !== q; + }); + list.unshift(q); + writeLocalHistory(list); + } + }); + }); + } + function init() { bindRemoteBoxes(document); + bindFullSearchBoxHistory(); bindNavbarSearchSlide(); var form = document.querySelector('form[data-aksrt-search-form="inline"]'); if (form) enhancePanel(form); diff --git a/assets/js/header-newadd.js b/assets/js/header-newadd.js index 8ef329c0..c835cfea 100644 --- a/assets/js/header-newadd.js +++ b/assets/js/header-newadd.js @@ -45,7 +45,7 @@ openMenu.classList.remove('is-open'); openMenu.setAttribute('hidden', ''); openMenu.setAttribute('aria-hidden', 'true'); - if (openToggle) openToggle.setAttribute('aria-expanded', 'false'); + if (openToggle && openToggle.setAttribute) openToggle.setAttribute('aria-expanded', 'false'); if (openRoot) openRoot.classList.remove('is-open'); // Return menu to original root so SSR structure stays consistent. if (openRoot && openMenu.parentNode !== openRoot) { @@ -60,6 +60,11 @@ var toggle = root.querySelector('.newadd-btns__toggle'); var menu = root.querySelector('.drop-newadd'); if (!toggle || !menu) return; + if (toggle.tagName === 'SPAN' || toggle.tagName === 'DIV') { + toggle.setAttribute('role', 'button'); + toggle.setAttribute('tabindex', '0'); + if (!toggle.hasAttribute('aria-expanded')) toggle.setAttribute('aria-expanded', 'false'); + } if (openRoot && openRoot !== root) closeMenu(); @@ -75,7 +80,7 @@ menu.setAttribute('aria-hidden', 'false'); menu.classList.add('is-open'); root.classList.add('is-open'); - toggle.setAttribute('aria-expanded', 'true'); + if (toggle.setAttribute) toggle.setAttribute('aria-expanded', 'true'); placeMenu(toggle, menu); } @@ -136,6 +141,11 @@ var toggle = root.querySelector('.newadd-btns__toggle'); var menu = root.querySelector('.drop-newadd'); if (!toggle || !menu) return; + if (toggle.tagName === 'SPAN' || toggle.tagName === 'DIV') { + toggle.setAttribute('role', 'button'); + toggle.setAttribute('tabindex', '0'); + if (!toggle.hasAttribute('aria-expanded')) toggle.setAttribute('aria-expanded', 'false'); + } // CSS hover no longer drives this control. root.classList.add('is-js-portal'); diff --git a/header.php b/header.php index 52d7f818..168e357b 100644 --- a/header.php +++ b/header.php @@ -49,6 +49,11 @@ if (!empty($aksrt_header_settings['scroll_down_hide']) && !empty($aksrt_header_s $aksrt_body_classes[] = 'header-layout-' . $aksrt_header_settings['layout']; $aksrt_body_classes[] = 'mobile-header-' . $aksrt_header_settings['mobile_layout']; $aksrt_body_classes[] = 'mobile-nav-' . $aksrt_header_settings['mobile_align']; + // header_nav_style → body class for transparent / blur chrome. + $aksrt_nav_style = sanitize_key((string) ($aksrt_header_settings['nav_style'] ?? 'default')); + if (in_array($aksrt_nav_style, ['transparent', 'blur'], true)) { + $aksrt_body_classes[] = 'header-nav-style-' . $aksrt_nav_style; + } // Global background system. $aksrt_bg_settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_global_background_settings() : []; diff --git a/inc/helpers/aksrt-header-menu-buttons.php b/inc/helpers/aksrt-header-menu-buttons.php index 8f504b1c..a21deba0 100644 --- a/inc/helpers/aksrt-header-menu-buttons.php +++ b/inc/helpers/aksrt-header-menu-buttons.php @@ -26,6 +26,7 @@ if (!function_exists('aksrt_get_header_newposts_config')) : $shop = \AKSRT\Settings\Settings::get_shop_settings(); if (!empty($forum['enabled'])) { $types[] = 'topic'; + $types[] = 'plate'; } if (!empty($shop['enabled'])) { $types[] = 'product'; @@ -87,6 +88,28 @@ if (!function_exists('aksrt_get_new_add_actions')) : 'label' => __('发布商品', 'aksrt'), 'icon' => 'fas fa-shopping-bag', ]; + } elseif ($type === 'plate') { + // zibll bbs_plate → AKSRT forum plate CPT (admin create for entitled users). + $can = current_user_can('manage_options'); + if (!$can && class_exists('\AKSRT\Forum\Forum')) { + try { + $forum = \AKSRT\Forum\Forum::get_instance(); + if (method_exists($forum, 'forum_user_can')) { + $can = (bool) $forum->forum_user_can(get_current_user_id(), 'plate_create'); + } + } catch (\Throwable $e) { + $can = false; + } + } + if (!$can || !post_type_exists('aksrt_forum_plate')) { + continue; + } + $out[] = [ + 'type' => 'plate', + 'url' => admin_url('post-new.php?post_type=aksrt_forum_plate'), + 'label' => __('创建版块', 'aksrt'), + 'icon' => 'fas fa-th-large', + ]; } } return $out; diff --git a/inc/helpers/header/aksrt-header-render.php b/inc/helpers/header/aksrt-header-render.php index 2943d872..146ba42f 100644 --- a/inc/helpers/header/aksrt-header-render.php +++ b/inc/helpers/header/aksrt-header-render.php @@ -148,12 +148,17 @@ if (!function_exists('aksrt_header')) : } $nav_class = [ - 'navbar', - 'navbar-top', - $ctx['mobile_layout'], - ]; + 'navbar', + 'navbar-top', + $ctx['mobile_layout'], +]; +$style_key = sanitize_key((string) ($ctx['settings']['nav_style'] ?? 'default')); +if (in_array($style_key, ['transparent', 'blur'], true)) { + $header_class[] = 'nav-style-' . $style_key; + $nav_class[] = 'nav-style-' . $style_key; +} - $nav_style = ''; +$nav_style = ''; if ((int) $ctx['max_width'] > 0) { $nav_style = ' style="--nav-max-width:' . (int) $ctx['max_width'] . 'px"'; } @@ -180,10 +185,10 @@ if (!function_exists('aksrt_header')) : echo ''; if (!empty($ctx['mobile_drawer'])) { - aksrt_nav_mobile_shell($ctx); - } + aksrt_nav_mobile_shell($ctx); + } - // Layout 1/3 search slide (zibll .navbar-search). Layout-2 uses inline menu_search. + // Layout 1/3 search slide (zibll .navbar-search). Layout-2 uses inline menu_search. if (!empty($ctx['search_enabled'])) { $navbar_search = locate_template('templates/parts/navbar-search.php'); if ($navbar_search) { diff --git a/src/AKSRT/Admin/Schema/SchemaPageDisplay.php b/src/AKSRT/Admin/Schema/SchemaPageDisplay.php index 3537de99..d1544a02 100644 --- a/src/AKSRT/Admin/Schema/SchemaPageDisplay.php +++ b/src/AKSRT/Admin/Schema/SchemaPageDisplay.php @@ -62,11 +62,13 @@ class SchemaPageDisplay { ['label' => '文章', 'value' => 'post'], ['label' => '帖子', 'value' => 'topic'], ['label' => '商品', 'value' => 'product'], + ['label' => '版块', 'value' => 'plate'], ]], ['key' => 'header_user_newposts_btn', 'label' => '用户卡发布类型', 'type' => 'select', 'default' => 'post', 'options' => [ ['label' => '文章', 'value' => 'post'], ['label' => '帖子', 'value' => 'topic'], ['label' => '商品', 'value' => 'product'], + ['label' => '版块', 'value' => 'plate'], ]], ['key' => 'header_user_newposts_text', 'label' => '用户卡发布文案', 'type' => 'input', 'default' => '发布内容'], ]], diff --git a/tools/nav-zibll-acceptance.md b/tools/nav-zibll-acceptance.md index 5e21626f..fe2bf4ab 100644 --- a/tools/nav-zibll-acceptance.md +++ b/tools/nav-zibll-acceptance.md @@ -40,3 +40,14 @@ Branch: `worktree-nav-zibll-full-1to1` ## Contract report - `tools/nav-zibll-contract-report.txt` should list search DOM PASS after smoke. + + +## Code-complete (browser QA still manual) +The following are implemented in code; remaining checkboxes above need live WP QA: +- menu_search zibll DOM + remote-box +- L1/3 `.navbar-search` slide + toggle isolation +- `header_nav_style` transparent/blur classes + CSS +- unified `header_newposts_enabled` + plate type + jb skin +- user box admin 4-row +- logged-in history AJAX save on top search submit +- admin `npm run build` executed in this worktree diff --git a/tools/nav-zibll-contract-report.txt b/tools/nav-zibll-contract-report.txt index dd35a495..49cc583e 100644 --- a/tools/nav-zibll-contract-report.txt +++ b/tools/nav-zibll-contract-report.txt @@ -1,6 +1,7 @@ -PASS deep nav contracts (search-box DOM + remote menu_search + navbar-search slide + unified newposts) -- aksrt_get_search_box / aksrt_get_search_posts present -- menu_search AJAX wraps .dropdown-menu.hover-show-con > .search-input -- layout1/3 templates/parts/navbar-search.php -- header_newposts_enabled single switch; publish_btn mirrors -- user box admin row: 主题设置/模块布局/商城中心/后台 +PASS deep nav contracts (full-push P0/P1) +- search-box DOM + remote menu_search + navbar-search slide +- header_nav_style transparent/blur body+header classes + CSS +- logged-in search history save via aksrt_search_history +- publish types post/topic/product/plate; jb-* skin +- admin SPA rebuilt (assets/admin/build) +- dead aksrt-menu-search-* private CSS removed