From fc50e57bc984bc84a846d6e77064b7e9ed7942a1 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 22 Jul 2026 11:40:17 +0800 Subject: [PATCH 1/3] feat(nav): complete remaining zibll parity (search, i18n, senior menus, CSS) - Remove obsolete dual-path nav CSS; add utilities + language dropdown styles - Wire header/mobile translate switcher from i18n config - Layout-2 inline search hot keywords panel; nav_folding on resize - NavMenuOptions JSON editors for graphic_card / multi_column_links - Section-navs zibll posts-nav-lists; page-navs enqueue; mobile article TOC box - SPA/PHP user-card publish type; rebuild admin assets --- .../src/schema/sections/page-display/index.js | 6 + assets/css/frontend/nav.css | 7 +- assets/css/frontend/nav/base.css | 577 ------------- assets/css/frontend/nav/cards-submenu.css | 128 --- .../css/frontend/nav/dark-mode-mega-menu.css | 12 - assets/css/frontend/nav/hover-effects.css | 35 - assets/css/frontend/nav/mega-submenu.css | 28 - assets/css/frontend/nav/menu-item-badge.css | 41 - assets/css/frontend/nav/menu-item-icon.css | 20 - .../nav/mobile-theme-toggle-in-drawer.css | 18 - assets/css/frontend/nav/mobile-toggle.css | 814 ------------------ .../nav/nav-fixed-mobile-fallback.css | 10 - .../css/frontend/nav/responsive-mega-menu.css | 109 --- assets/css/frontend/nav/zib-utilities.css | 132 +++ assets/js/core/nav.js | 75 +- assets/js/section-navs.js | 282 +++--- inc/helpers/aksrt-i18n-switch.php | 53 ++ inc/helpers/header/aksrt-header-mobile.php | 7 + inc/helpers/header/aksrt-header-navbar.php | 39 +- src/AKSRT/Admin/NavMenuOptions.php | 165 +++- src/AKSRT/Admin/Schema/SchemaPageDisplay.php | 5 + src/AKSRT/Core/ScriptsPart1b.php | 11 + 22 files changed, 580 insertions(+), 1994 deletions(-) delete mode 100644 assets/css/frontend/nav/base.css delete mode 100644 assets/css/frontend/nav/cards-submenu.css delete mode 100644 assets/css/frontend/nav/dark-mode-mega-menu.css delete mode 100644 assets/css/frontend/nav/hover-effects.css delete mode 100644 assets/css/frontend/nav/mega-submenu.css delete mode 100644 assets/css/frontend/nav/menu-item-badge.css delete mode 100644 assets/css/frontend/nav/menu-item-icon.css delete mode 100644 assets/css/frontend/nav/mobile-theme-toggle-in-drawer.css delete mode 100644 assets/css/frontend/nav/mobile-toggle.css delete mode 100644 assets/css/frontend/nav/nav-fixed-mobile-fallback.css delete mode 100644 assets/css/frontend/nav/responsive-mega-menu.css create mode 100644 assets/css/frontend/nav/zib-utilities.css diff --git a/assets/admin/src/schema/sections/page-display/index.js b/assets/admin/src/schema/sections/page-display/index.js index 8b2f05aa..659a813a 100644 --- a/assets/admin/src/schema/sections/page-display/index.js +++ b/assets/admin/src/schema/sections/page-display/index.js @@ -37,6 +37,7 @@ export const pageDisplaySchemas = { header_nav_pay_vip: true, header_nav_user_pay_vip: true, header_newposts_types: ['post'], + header_user_newposts_btn: 'post', header_user_newposts_text: '发布内容', mobile_header_layout: 'center', mobile_navbar_align: 'left', @@ -124,6 +125,11 @@ export const pageDisplaySchemas = { { label: '帖子', value: 'topic' }, { label: '商品', value: 'product' }, ], 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' }, + ] }, { key: 'header_user_newposts_text', label: '用户卡发布文案', type: 'input', default: '发布内容' }, { key: 'header_nav_pay_vip', label: '导航栏开通会员按钮', type: 'switch', default: true, help: '顶部导航显示开通会员' }, { key: 'header_nav_user_pay_vip', label: '用户框开通会员按钮', type: 'switch', default: true }, diff --git a/assets/css/frontend/nav.css b/assets/css/frontend/nav.css index 10fd9882..207f608b 100644 --- a/assets/css/frontend/nav.css +++ b/assets/css/frontend/nav.css @@ -2,10 +2,7 @@ @import url("nav/zib-navbar-core.css"); @import url("nav/zib-mobile-navbar.css"); @import url("nav/zib-senior-submenu.css"); -@import url("nav/dark-mode-submenu.css"); -@import url("nav/dark-mode-mega-menu.css"); +@import url("nav/zib-utilities.css"); @import url("nav/header-menu-buttons.css"); @import url("nav/header-userbox.css"); -@import url("nav/menu-item-icon.css"); -@import url("nav/menu-item-badge.css"); -@import url("nav/hover-effects.css"); +@import url("nav/dark-mode-submenu.css"); diff --git a/assets/css/frontend/nav/base.css b/assets/css/frontend/nav/base.css deleted file mode 100644 index 949134e5..00000000 --- a/assets/css/frontend/nav/base.css +++ /dev/null @@ -1,577 +0,0 @@ -/* Zibll body.nav-fixed: hard 88px reserve (main.css), not bare --nav-height. - * Nav bar itself is ~60px; extra space matches zibll chrome/shadow gap. - * --nav-fixed-reserve is shared with the hero pull-up so they cancel exactly - * (zibll: body padding-top 88px + .slide-header { margin-top: -90px }). */ -body.nav-fixed { - --nav-fixed-reserve: 88px; - padding-top: var(--nav-fixed-reserve); -} - -@media (max-width: 768px) { - body.nav-fixed { - --nav-fixed-reserve: 72px; - padding-top: var(--nav-fixed-reserve); - } -} - -@media (max-width: 640px) { - body.nav-fixed { - --nav-fixed-reserve: 60px; - padding-top: var(--nav-fixed-reserve); - } -} - -/* Fixed nav without hero overlay: take header out of flow. - body.nav-fixed already reserves space via padding-top. */ -body.nav-fixed .header:not(.show-slide) { - position: fixed; - top: 0; - left: 0; - right: 0; - width: 100%; - z-index: 1000; -} - -/* Nav lives inside the fixed header — don't keep sticky (double offset). */ -body.nav-fixed .header:not(.show-slide) .site-nav, -body.nav-fixed .header:not(.show-slide) .navbar.navbar-top { - position: relative; - top: auto; -} - -/* - * Non-home pages have no hero under the fixed header. Keep the first content - * (breadcrumb / article) clearly below the bar even if a fluid widget zone is - * empty or a child uses negative margin. - */ -body.nav-fixed:not(:has(.header.show-slide)) .fluid-widget-wrap:first-of-type:empty + main.container, -body.nav-fixed:not(:has(.header.show-slide)) main.container:first-of-type { - scroll-margin-top: calc(var(--nav-fixed-reserve, 88px) + 8px); -} - -/* Hero sits under transparent fixed/absolute header. - * MUST cancel the full --nav-fixed-reserve (88/72/60), not just --nav-height (60). - * Using only -60px against 88px padding left a ~28px blank strip above the hero. */ -body.nav-fixed .header-slider-container, -body.nav-fixed .header-components-container, -body.nav-fixed .header-components.slide-header { - margin-top: calc(var(--nav-fixed-reserve, 88px) * -1) !important; - position: relative; - z-index: 1; -} - -/* ============================================================ - * AKSRT Navigation — Zibll-aligned - * - * - 高度 60px (--nav-height) - * - 毛玻璃 blur-bg(saturate(5) blur(20px)) - * - 透明叠 slider → 滚动后恢复实底 - * - 滚动隐藏(scroll-down-hide 等价) - * ============================================================ */ - -.site-nav { - position: sticky; - top: 0; - z-index: 1000; - min-height: var(--nav-height, 60px); - background-color: var(--blur-bg); - border-bottom: 1px solid rgba(255, 255, 255, 0.45); - backdrop-filter: saturate(5) blur(20px); - -webkit-backdrop-filter: saturate(5) blur(20px); - transition: background-color 0.25s ease, - border-color 0.25s ease, - color 0.25s ease, - box-shadow 0.25s ease, - transform 0.3s ease; /* 滚动隐藏过渡 */ -} - -/* 滚动隐藏头部 — 对齐 zibll .scroll-down.nav-fixed .header.scroll-down-hide */ -.site-nav.is-hidden { - transform: translateY(-105%); -} - -.site-nav--static { - position: relative; -} - -.site-nav--transparent { - background: transparent; /* 对齐 zibll .header.show-slide: 透明覆盖在 slider 上 */ - border-bottom-color: transparent; -} - -.site-nav--transparent:not(.is-scrolled) { - --header-color: #fff; /* 对齐 zibll --header-color:#fff */ - color: #fff; -} - -.site-nav--transparent:not(.is-scrolled) a { - color: inherit; -} - -.site-nav--transparent.is-scrolled { - background: var(--blur-bg); /* 滚动后恢复毛玻璃背景 */ - border-bottom-color: var(--color-line-soft); -} - -.site-nav--blur { - background: var(--blur-bg); - backdrop-filter: saturate(5) blur(20px); - -webkit-backdrop-filter: saturate(5) blur(20px); - border-bottom-color: transparent; -} - -html.dark .site-nav--transparent:not(.is-scrolled), -html.dark .site-nav--blur:not(.is-scrolled), -body.dark-theme .site-nav--transparent:not(.is-scrolled), -body.dark-theme .site-nav--blur:not(.is-scrolled) { - background: transparent; - border-bottom-color: transparent; -} - -html.dark .site-nav--transparent.is-scrolled, -html.dark .site-nav--blur.is-scrolled, -body.dark-theme .site-nav--transparent.is-scrolled, -body.dark-theme .site-nav--blur.is-scrolled { - background: var(--blur-bg); - border-bottom-color: var(--color-line-soft); -} - -/* 滚动后追加阴影 — 对齐 zibll .body-scroll .header */ -.site-nav.is-scrolled { - box-shadow: 0 4px 10px var(--main-shadow, var(--shadow-sm)); -} - -/* WordPress Admin Bar 已全局禁用,无需偏移规则 */ - -/* ============================================================ - * Inner / Row layout - * ============================================================ */ -.site-nav__inner { - max-width: var(--nav-max-width, var(--container)); - margin: 0 auto; - padding: 0 16px; - display: flex; - flex-direction: column; - gap: 0; -} - -.site-nav__row { - display: flex; - align-items: center; - justify-content: flex-start; - gap: 12px; - width: 100%; - min-height: var(--nav-height, 60px); -} - -/* ============================================================ - * Brand - * ============================================================ */ -.site-brand { - display: inline-flex; - align-items: center; - gap: 10px; - min-width: 0; - max-width: min(48vw, 360px); - padding: 7px 10px; - color: var(--color-ink); - font-size: 17px; - font-weight: 700; - letter-spacing: -0.01em; - flex-shrink: 0; - text-decoration: none; -} - -.site-brand:hover { - color: var(--color-ink); - text-decoration: none; -} - -.site-brand--has-logo { - gap: 0; -} - -.site-brand__logo { - width: 36px; - height: 36px; - border-radius: 8px; - display: flex; - align-items: center; - justify-content: center; - font-weight: 800; - font-size: 13px; - color: var(--color-on-primary, #fff); - background: linear-gradient(135deg, var(--color-primary), var(--color-primary-active, var(--color-primary-dark))); - letter-spacing: 0.02em; - overflow: hidden; -} - -.site-brand__media { - display: block; - flex: 0 1 auto; - max-width: 100%; - padding: 0 30px; - line-height: 0; - box-sizing: border-box; - background: transparent !important; - border: 0; - border-radius: 0; - box-shadow: none; - filter: none !important; - opacity: 1; -} - -.site-brand__img { - display: block; - height: 36px; - width: auto; - max-width: 100%; - object-fit: contain; - object-position: left center; - background: transparent !important; - border-radius: 0; - box-shadow: none; - filter: none !important; - mix-blend-mode: normal !important; - opacity: 1; - transform: none !important; -} - -.site-brand__name { - font-weight: 700; - letter-spacing: -0.01em; - white-space: nowrap; -} - -/* Light/dark logo: single adaptive img (white-src/dark-src via theme.js) */ -.navbar-logo > img, -.navbar-logo .site-brand__logo-img, -.site-brand .site-brand__logo-img, -.site-brand__logo-img, -.site-brand .navbar-logo img { - height: 36px !important; - width: auto !important; - max-width: min(180px, 40vw) !important; - max-height: 36px !important; - padding: 0 !important; - object-fit: contain !important; - display: inline-block; - vertical-align: middle; -} - -/* Legacy dual-img classes (if any remain elsewhere) */ -.site-brand__logo-img--dark { - display: none; -} - -html.dark .site-brand__logo-img--light, -[data-theme="dark"] .site-brand__logo-img--light, -body.dark-theme .site-brand__logo-img--light { - display: none; -} - -html.dark .site-brand__logo-img--dark, -[data-theme="dark"] .site-brand__logo-img--dark, -body.dark-theme .site-brand__logo-img--dark { - display: inline-block; -} - -/* ============================================================ - * Desktop menu - * ============================================================ */ -ul.nav-desktop { - display: none; - list-style: none; - margin: 0; - padding: 0; - align-items: center; - flex: 1 1 auto; - min-width: 0; -} - -.nav-desktop--fallback { - display: none; - align-items: center; - flex: 1 1 auto; - min-width: 0; -} - -ul.nav-desktop a, -.nav-desktop--fallback a { - color: var(--color-ink-3); - font-size: 14px; - font-weight: 500; - padding: 0 12px; /* 对齐 zibll:仅水平 padding,垂直居中靠 flex 行高 */ - border-radius: 4px; - transition: color var(--dur-fast, 0.15s) var(--ease), - background var(--dur-fast, 0.15s) var(--ease); -} - -/* Hover — 对齐 zibll: 仅变色,无背景 */ -ul.nav-desktop a:hover, -.nav-desktop--fallback a:hover { - color: var(--color-primary); - background: transparent; -} - -/* Active page — 对齐 zibll: 仅变色(指示器走下方胶囊条) */ -ul.nav-desktop .current-menu-item > a, -ul.nav-desktop .current_page_item > a { - color: var(--color-primary); - background: transparent; -} - -/* ============================================================ - * Desktop submenu (dropdown) - * ============================================================ */ -ul.nav-desktop .menu-item { - position: relative; -} - -/* 顶部 hover/current 胶囊指示器 — 对齐 zibll .navbar-nav>li:before - (5px 高、底部 15px 圆角,从 header 顶缘垂下)。 - stretch 链(row→ul→li)让 li 充满 60px 行高,::before top:0 即锚定 header 顶部; - ::before 为 absolute 不进入 flex 流,不挤压链接。 */ -ul.nav-desktop { - align-self: stretch; -} - -ul.nav-desktop > .menu-item { - align-self: stretch; - display: flex; - align-items: center; -} - -ul.nav-desktop > .menu-item::before { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 5px; - background: var(--focus-color, var(--color-primary)); - border-radius: 0 0 15px 15px; - opacity: 0; - transition: opacity 0.3s; - pointer-events: none; -} - -ul.nav-desktop > .menu-item:hover::before, -ul.nav-desktop > .current-menu-item::before, -ul.nav-desktop > .current_page_item::before { - opacity: 1; -} - -ul.nav-desktop .sub-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 998; /* 保留较高 z-index(zibll 用 99,AKSRT 栈更深,避免被覆盖) */ - min-width: 140px; - list-style: none; - margin: 8px 0 0; - padding: 8px 6px; - background: var(--main-bg-color, #fff); - border: none; - border-radius: var(--main-radius, var(--radius, 8px)); - box-shadow: var(--shadow-lg); - opacity: 0; - visibility: hidden; - transform: translateY(6px); - transition: opacity 0.4s ease, transform 0.4s ease, visibility 0.4s ease; -} - -ul.nav-desktop .menu-item:hover > .sub-menu { - opacity: 1; - visibility: visible; - transform: translateY(0); -} - -ul.nav-desktop .sub-menu .menu-item > a { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 15px; - font-size: 14px; - border-radius: 4px; - white-space: nowrap; -} - -ul.nav-desktop .sub-menu .menu-item > a:hover { - background: var(--color-primary-soft, rgba(240, 68, 148, 0.08)); - color: var(--color-primary); -} - -/* Nested submenus fly out to the right — 对齐 zibll top:-10px,去 margin-left */ -ul.nav-desktop .sub-menu .sub-menu { - top: -10px; - left: 100%; - margin: 0; -} - - - -/* ============================================================ - * Zibll .header.show-slide — transparent gradient over hero - * ============================================================ */ -.header.show-slide { - --header-bg: linear-gradient(0, rgba(0, 0, 0, 0) 5%, rgba(0, 0, 0, 0.1) 80%); - --header-color: #fff; - -webkit-backdrop-filter: unset; - backdrop-filter: unset; -} - -body.nav-fixed .header.show-slide { - position: absolute !important; - top: 0; - left: 0; - right: 0; - width: 100%; - z-index: 30; - background: transparent !important; - box-shadow: none !important; - border: 0 !important; -} - -/* Nav inside absolute header must not use sticky (avoids double offset) */ -body.nav-fixed .header.show-slide .site-nav, -body.nav-fixed .header.show-slide .navbar.navbar-top { - position: relative !important; - top: auto !important; - background: var(--header-bg, transparent) !important; - border-bottom-color: transparent !important; - box-shadow: none !important; - color: var(--header-color, #fff); - backdrop-filter: none !important; - -webkit-backdrop-filter: none !important; -} - -.header.show-slide .navbar.navbar-top, -.header.show-slide .site-nav { - background: var(--header-bg, transparent); - border-bottom-color: transparent; - box-shadow: none; - color: var(--header-color, #fff); - backdrop-filter: none; - -webkit-backdrop-filter: none; -} - -.header.show-slide .site-nav:not(.is-scrolled) a, -.header.show-slide .site-nav:not(.is-scrolled) .icon-btn, -.header.show-slide .site-nav:not(.is-scrolled) .site-brand__name { - color: #fff; -} - -.header.show-slide .site-nav.is-scrolled, -.site-nav.is-scrolled { - background: var(--blur-bg); - color: var(--header-color, var(--color-ink-2)); - border-bottom-color: rgba(255, 255, 255, 0.45); - backdrop-filter: saturate(5) blur(20px); - -webkit-backdrop-filter: saturate(5) blur(20px); - box-shadow: 0 4px 10px var(--main-shadow, rgba(116, 116, 116, 0.08)); -} - -.navbar.navbar-top { - margin: 0; - border-radius: 0 !important; - font-size: 15px; -} - -.container-header, -.container-footer { - max-width: calc(var(--mian-max-width, var(--container, 1200px)) + 180px); - margin-left: auto; - margin-right: auto; - width: 100%; - padding-left: 15px; - padding-right: 15px; -} - -.navbar-top .navbar-brand { - padding: 7px 10px; -} - -.navbar-logo > img, -.navbar-logo .site-brand__logo-img, -.site-brand .site-brand__logo-img, -.site-brand__logo-img { - height: 36px !important; - width: auto !important; - max-width: min(180px, 40vw) !important; - max-height: 36px !important; - padding: 0 !important; - object-fit: contain !important; -} - -.navbar-top .navbar-nav, -.navbar-top ul.nav-desktop { - margin-left: 20px; - margin-right: 20px; - display: flex; - align-items: center; - gap: 0; - list-style: none; - padding: 0; -} - -.navbar-top .navbar-nav > li > a, -.navbar-top ul.nav-desktop > li > a { - padding-left: 10px; - padding-right: 10px; - border-radius: 4px; - display: inline-flex !important; - align-items: center; - min-height: 36px; - font-size: 15px; -} - -.navbar-top .site-nav__row { - display: flex; - align-items: center; - width: 100%; - min-height: var(--nav-height, 60px); -} - -.navbar-top .nav-actions { - margin-left: auto; - display: flex; - align-items: center; - gap: 8px; - overflow: visible; - position: relative; - z-index: 25; -} - -.scroll-down.nav-fixed .header.scroll-down-hide, -body.scroll-down.nav-fixed .header.scroll-down-hide { - transform: translate3d(0, -105%, 0); - -webkit-backdrop-filter: none; - backdrop-filter: none; -} - - -/* Layout-1 style: keep search as icon; hide expanded desktop form */ -@media (min-width: 992px) { - .navbar-top .nav-searchbox--desktop { - display: none !important; - } -} - -/* Transparent white controls on gradient hero */ -.header.show-slide .site-nav:not(.is-scrolled) .btn-outline { - color: #fff; - border-color: rgba(255, 255, 255, 0.55); - background: transparent; -} - -.header.show-slide .site-nav:not(.is-scrolled) .btn-outline:hover { - color: #fff; - border-color: #fff; - background: rgba(255, 255, 255, 0.12); -} - -.header.show-slide .site-nav:not(.is-scrolled) .nav-user__guest-avatar, -.header.show-slide .site-nav:not(.is-scrolled) .icon-btn { - color: #fff; -} diff --git a/assets/css/frontend/nav/cards-submenu.css b/assets/css/frontend/nav/cards-submenu.css deleted file mode 100644 index 272b996d..00000000 --- a/assets/css/frontend/nav/cards-submenu.css +++ /dev/null @@ -1,128 +0,0 @@ -/* Cards submenu */ -.aksrt-nav__cards { - display: grid; - gap: 12px; - padding: 8px; -} - -.aksrt-nav__card { - display: flex; - align-items: center; - gap: 12px; - padding: 10px 12px; - border-radius: 6px; - text-decoration: none; - color: inherit; - transition: background 0.15s; -} - -.aksrt-nav__card:hover { - background: var(--color-primary-soft, rgba(240, 68, 148, 0.06)); -} - -.aksrt-nav__card-icon { - width: 36px; - height: 36px; - border-radius: 8px; - display: flex; - align-items: center; - justify-content: center; - background: var(--color-primary-soft, #e6f4ff); - color: var(--color-primary); - font-size: 16px; - flex-shrink: 0; -} - -.aksrt-nav__card-text { - flex: 1; - min-width: 0; -} - -.aksrt-nav__card-title { - font-size: 13px; - font-weight: 600; - color: var(--color-ink); - margin: 0; -} - -.aksrt-nav__card-desc { - font-size: 11px; - color: var(--color-ink-4); - margin: 2px 0 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -/* ============================================================ - * 导航溢出「更多」下拉 - * ============================================================ */ -.aksrt-nav-more { - position: relative; -} - -.aksrt-nav-more__btn { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 6px 12px; - border: none; - background: none; - color: var(--color-ink-2); - font-size: 14px; - font-weight: 500; - cursor: pointer; - border-radius: var(--radius, 8px); - transition: background 0.15s, color 0.15s; -} - -.aksrt-nav-more__btn:hover { - background: var(--color-primary-soft, rgba(240, 68, 148, 0.08)); - color: var(--color-primary); -} - -.aksrt-nav-more__dropdown { - display: none; - position: absolute; - top: 100%; - right: 0; - min-width: 160px; - padding: 6px 0; - background: var(--surface-1, #fff); - border: 1px solid var(--color-line, rgba(50, 50, 50, 0.06)); - border-radius: var(--radius, 8px); - box-shadow: var(--shadow-lg, 0 0 10px rgba(116, 116, 116, 0.12)); - z-index: var(--z-dropdown, 100); - list-style: none; - margin: 4px 0 0; -} - -.aksrt-nav-more__dropdown.is-open { - display: block; -} - -.aksrt-nav-more__dropdown li { - display: block; -} - -.aksrt-nav-more__dropdown li a, -.aksrt-nav-more__dropdown li button { - display: block; - width: 100%; - padding: 8px 16px; - border: none; - background: none; - color: var(--color-ink-2); - font-size: 14px; - text-align: left; - text-decoration: none; - cursor: pointer; - transition: background 0.15s, color 0.15s; -} - -.aksrt-nav-more__dropdown li a:hover, -.aksrt-nav-more__dropdown li button:hover { - background: var(--color-primary-soft, rgba(240, 68, 148, 0.08)); - color: var(--color-primary); -} - diff --git a/assets/css/frontend/nav/dark-mode-mega-menu.css b/assets/css/frontend/nav/dark-mode-mega-menu.css deleted file mode 100644 index 28994298..00000000 --- a/assets/css/frontend/nav/dark-mode-mega-menu.css +++ /dev/null @@ -1,12 +0,0 @@ -/* Dark mode Mega Menu */ -html.dark ul.nav-desktop .mega-menu-item > .mega-menu__panel, -body.dark-theme ul.nav-desktop .mega-menu-item > .mega-menu__panel { - background: var(--surface-page); - border-color: var(--color-line, #2d2d3f); -} - -html.dark .mega-menu__panel > .menu-item:hover, -body.dark-theme .mega-menu__panel > .menu-item:hover { - background: var(--surface-2); -} - diff --git a/assets/css/frontend/nav/hover-effects.css b/assets/css/frontend/nav/hover-effects.css deleted file mode 100644 index 2e127318..00000000 --- a/assets/css/frontend/nav/hover-effects.css +++ /dev/null @@ -1,35 +0,0 @@ -/* Hover effects */ -.aksrt-nav__item--hover-underline > a::after { - content: ''; - position: absolute; - bottom: 0; - left: 50%; - width: 0; - height: 2px; - background: var(--color-primary); - transition: width 0.2s ease, left 0.2s ease; - border-radius: 1px; -} - -.aksrt-nav__item--hover-underline:hover > a::after { - width: 60%; - left: 20%; -} - -.aksrt-nav__item--hover-background > a { - transition: background-color 0.2s; - border-radius: 4px; -} - -.aksrt-nav__item--hover-background:hover > a { - background-color: var(--color-primary-soft, rgba(240, 68, 148, 0.08)); -} - -.aksrt-nav__item--hover-color > a { - transition: color 0.2s; -} - -.aksrt-nav__item--hover-color:hover > a { - color: var(--color-primary); -} - diff --git a/assets/css/frontend/nav/mega-submenu.css b/assets/css/frontend/nav/mega-submenu.css deleted file mode 100644 index d41a4464..00000000 --- a/assets/css/frontend/nav/mega-submenu.css +++ /dev/null @@ -1,28 +0,0 @@ -/* Mega submenu */ -.aksrt-nav__mega { - position: absolute; - top: 100%; - left: 0; - z-index: 1001; - background: var(--surface-1); - border: 1px solid var(--color-line); - border-radius: var(--radius); - box-shadow: var(--shadow-lg); - padding: 16px; - opacity: 0; - visibility: hidden; - transform: translateY(8px); - transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease; -} - -.aksrt-nav__item:hover > .aksrt-nav__mega { - opacity: 1; - visibility: visible; - transform: translateY(0); -} - -.aksrt-nav__mega--columns { - display: grid; - gap: 16px; -} - diff --git a/assets/css/frontend/nav/menu-item-badge.css b/assets/css/frontend/nav/menu-item-badge.css deleted file mode 100644 index 5a6b5379..00000000 --- a/assets/css/frontend/nav/menu-item-badge.css +++ /dev/null @@ -1,41 +0,0 @@ -/* Menu item badge */ -.aksrt-nav__badge { - display: inline-flex; - align-items: center; - justify-content: center; - font-size: 10px; - font-weight: 600; - line-height: 1; - padding: 2px 5px; - border-radius: 3px; - margin-left: 4px; - white-space: nowrap; - vertical-align: middle; - transform: translateY(-1px); -} - -.aksrt-nav__badge--new { - color: #fff; - background: var(--color-error); -} - -.aksrt-nav__badge--hot { - color: #fff; - background: var(--color-warning); -} - -.aksrt-nav__badge--info { - color: #fff; - background: var(--color-info); -} - -.aksrt-nav__badge--success { - color: #fff; - background: var(--color-success); -} - -.aksrt-nav__badge--vip { - color: #fff; - background: linear-gradient(135deg, var(--color-warning), var(--color-error)); -} - diff --git a/assets/css/frontend/nav/menu-item-icon.css b/assets/css/frontend/nav/menu-item-icon.css deleted file mode 100644 index 67ccce61..00000000 --- a/assets/css/frontend/nav/menu-item-icon.css +++ /dev/null @@ -1,20 +0,0 @@ -/* Menu item icon */ -.aksrt-nav__icon { - font-size: 0.85em; - width: 1.2em; - text-align: center; - margin-right: 4px; - opacity: 0.7; - transition: opacity 0.2s; -} - -.aksrt-nav__item--has-icon > a { - display: inline-flex; - align-items: center; - gap: 4px; -} - -.aksrt-nav__item--has-icon:hover .aksrt-nav__icon { - opacity: 1; -} - diff --git a/assets/css/frontend/nav/mobile-theme-toggle-in-drawer.css b/assets/css/frontend/nav/mobile-theme-toggle-in-drawer.css deleted file mode 100644 index 728acaf9..00000000 --- a/assets/css/frontend/nav/mobile-theme-toggle-in-drawer.css +++ /dev/null @@ -1,18 +0,0 @@ -/* Mobile theme toggle in drawer */ -.nav-drawer__theme-toggle { - padding: 0 20px 16px; -} - -.nav-drawer__theme-toggle .theme-toggle-mobile { - width: 100%; - min-height: 44px; - justify-content: center; - gap: 8px; - display: flex; - align-items: center; -} - -/* ============================================================ - * Menu Decoration — Icons, Badges, Hover Effects - * ============================================================ */ - diff --git a/assets/css/frontend/nav/mobile-toggle.css b/assets/css/frontend/nav/mobile-toggle.css deleted file mode 100644 index 134bc6c7..00000000 --- a/assets/css/frontend/nav/mobile-toggle.css +++ /dev/null @@ -1,814 +0,0 @@ -/* Mobile toggle */ -.nav-toggle { - display: flex; -} - -@media (max-width: 991px) { - .site-brand { - max-width: min(46vw, 311px); - } - - .site-brand__media { - padding: 0 8px; - } - - .nav-publish-btn { - display: none; - } -} - -/* Desktop search icon: hide on mobile (use drawer search instead) */ -@media (max-width: 767px) { - .site-brand { - max-width: min(62vw, 260px); - padding: 7px 0; - } - - .site-brand__media { - padding: 0; - } - - #search-toggle { - display: none; - } - /* Search panel only makes sense alongside the desktop search icon */ - .nav-search-panel { - display: none !important; - } -} - -@media (min-width: 768px) { - ul.nav-desktop, - .nav-desktop--fallback { - display: flex; - } - - .nav-auth { - display: flex; - } - - .nav-auth-dropdown { - display: flex; - } - - .nav-toggle { - display: none; - } -} - -/* ============================================================ - * Header Layout Variants (desktop ≥768px) - * Layout 1: Standard — brand left, nav flex-1, actions right - * Layout 2: Centered — brand left, nav centered with flex, actions right - * Layout 3: Compact — brand left, nav pushed right, actions right - * ============================================================ */ -@media (min-width: 768px) { - /* Layout 2: center the nav between brand and actions */ - body.header-layout-2 .nav-desktop { - flex: 1; - justify-content: center; - } - - body.header-layout-2 .nav-actions { - flex-shrink: 0; - } - - /* Layout 3: push nav to the right side */ - body.header-layout-3 .site-nav__row { - justify-content: flex-start; - } - - body.header-layout-3 .nav-desktop { - flex: 1; - justify-content: flex-end; - margin-left: 24px; - } - - body.header-layout-3 .nav-actions { - margin-left: 8px; - } -} - -.nav-mobile-strip { - display: none; - width: calc(100% + 8px); - margin: 0 -4px; - padding: 0 0 12px; -} - -.nav-mobile-strip__menu, -.nav-mobile-strip__menu ul { - display: flex; - align-items: center; - gap: 8px; - overflow-x: auto; - margin: 0; - padding: 0; - list-style: none; - scrollbar-width: none; - -ms-overflow-style: none; -} - -.nav-mobile-strip__menu::-webkit-scrollbar, -.nav-mobile-strip__menu ul::-webkit-scrollbar { - display: none; -} - -.nav-mobile-strip__menu a { - display: inline-flex; - align-items: center; - min-height: 34px; - padding: 0 12px; - border-radius: var(--radius-pill, 999px); - background: var(--surface-2); - color: var(--color-ink-2); - white-space: nowrap; - text-decoration: none; - font-size: 0.875rem; - font-weight: 500; -} - -.nav-mobile-strip__menu .current-menu-item > a, -.nav-mobile-strip__menu .current_page_item > a { - background: var(--color-primary); - color: var(--color-on-primary, #fff); -} - -@media (max-width: 767px) { - .nav-mobile-strip { - display: block; - } -} - -/* ============================================================ - * User avatar + dropdown - * ============================================================ */ -.nav-user { - position: relative; -} - -.nav-user__toggle { - width: 36px; - height: 36px; - border-radius: var(--radius-avatar); - padding: 0; - border: none; /* 对齐 zibll:头像无边框 */ - cursor: pointer; - /* Allow unread corner badge to paint outside the avatar circle. */ - overflow: visible; - background: var(--surface-2); - transition: box-shadow var(--dur-fast, 0.15s) var(--ease); -} - -.nav-user__toggle:hover { - box-shadow: 0 0 0 2px var(--color-primary-soft); -} - -.nav-user__toggle img { - width: 100%; - height: 100%; - object-fit: cover; - display: block; - border-radius: inherit; -} - -.user-dropdown { - position: absolute; - top: calc(100% + 8px); - right: 0; - width: 260px; - max-width: calc(100vw - 32px); - padding: 12px 0; - background: var(--surface-1); - border: 1px solid var(--color-line); - border-radius: var(--radius-lg, 8px); - box-shadow: var(--shadow-lg); - z-index: 1100; - opacity: 0; - visibility: hidden; - transform: translateY(-6px); - transition: opacity 0.2s, transform 0.2s, visibility 0.2s; -} - -.user-dropdown.is-open { - opacity: 1; - visibility: visible; - transform: translateY(0); -} - -.user-dropdown__profile { - display: flex; - align-items: center; - gap: 12px; - padding: 8px 16px 14px; -} - -.user-dropdown__profile img { - width: 44px; - height: 44px; - border-radius: var(--radius-avatar); - object-fit: cover; - flex-shrink: 0; -} - -.user-dropdown__name { - font-weight: 700; - font-size: 0.9375rem; - color: var(--color-ink); -} - -.user-dropdown__email { - font-size: 0.75rem; - color: var(--color-ink-4); - word-break: break-all; - margin-top: 2px; -} - -.user-dropdown__sep { - height: 1px; - background: var(--color-line); - margin: 6px 0; -} - -.user-dropdown a, -.user-dropdown button { - display: flex; - align-items: center; - gap: 10px; - width: 100%; - padding: 0 16px; - height: 44px; - border: none; - background: none; - font-size: 0.875rem; - color: var(--color-ink); - text-align: left; - cursor: pointer; - transition: background var(--dur-fast, 0.15s) var(--ease), - color var(--dur-fast, 0.15s) var(--ease); -} - -.user-dropdown a:hover, -.user-dropdown button:hover { - background: var(--surface-3); - color: var(--color-primary-active); -} - -/* dropdown 项内的 SVG 图标 */ -.user-dropdown__item svg { - width: 16px; - height: 16px; - flex-shrink: 0; - opacity: 0.85; -} - -.user-dropdown__item span:first-of-type:not(.user-dropdown__pill) { - flex: 1; - min-width: 0; -} - -/* 未读消息数 badge */ -.user-dropdown__badge { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 18px; - height: 18px; - padding: 0 5px; - margin-left: auto; - font-size: 11px; - font-weight: 600; - color: #fff; - background: var(--color-error, #ef4444); - border-radius: 9px; - line-height: 1; -} - -.user-dropdown__credits { - display: flex; - align-items: center; - gap: 10px; - padding: 0 16px; - height: 44px; - font-size: 0.875rem; - color: var(--color-ink-2); -} - -.user-dropdown__credits i { - color: var(--color-warning); - flex-shrink: 0; -} - -.user-dropdown__credits-label { - flex: 1; -} - -.user-dropdown__credits-value { - font-weight: 600; - color: var(--color-primary); -} - -.user-dropdown__logout { - color: var(--color-error) !important; -} - -.user-dropdown__logout svg { - color: var(--color-error); -} - -.user-dropdown__logout:hover { - background: rgba(239, 68, 68, 0.08) !important; - color: var(--color-error) !important; -} - -.user-dropdown .badge-dot { - margin-left: auto; - min-width: 8px; - height: 8px; - border-radius: var(--radius-pill); - background: var(--color-error); -} - -/* ============================================================ - * Search panel - * ============================================================ */ -.nav-search-panel { - display: none; - padding: 12px 0 16px; - border-top: 1px solid var(--color-line); -} - -.nav-search-panel.is-open { - display: block; -} - -.nav-searchbox { - margin: 0; -} - -.nav-searchbox__field { - display: flex; - align-items: center; - gap: 10px; - min-height: 42px; - padding: 0 6px 0 14px; - border: 1px solid var(--color-line); - border-radius: var(--radius-pill, 999px); - background: var(--surface-3); - box-sizing: border-box; - transition: border-color var(--dur-fast, 0.15s) var(--ease), box-shadow var(--dur-fast, 0.15s) var(--ease), background var(--dur-fast, 0.15s) var(--ease); -} - -.nav-searchbox__icon { - order: 1; - flex: 0 0 16px; - width: 16px; - height: 16px; - color: var(--color-ink-4); - pointer-events: none; -} - -.nav-searchbox__input { - order: 2; - flex: 1; - min-width: 0; - width: auto; - height: 40px; - padding: 0; - border: 0; - background: transparent; - color: var(--color-ink); - font-size: 0.9rem; - line-height: 1.2; - box-sizing: border-box; -} - -.nav-searchbox__input:focus { - outline: none; -} - -.nav-searchbox__input::placeholder { - color: var(--color-ink-4); -} - -.nav-searchbox__submit { - order: 3; - flex: 0 0 auto; - height: 30px; - min-width: 64px; - padding: 0 14px; - border: 0; - border-radius: var(--radius-pill, 999px); - background: var(--color-primary); - color: #fff; - font-size: 0.8125rem; - font-weight: 600; - line-height: 1; - white-space: nowrap; - cursor: pointer; -} - -.nav-searchbox__field:focus-within { - border-color: var(--color-primary); - box-shadow: 0 0 0 3px var(--color-primary-soft); - background: var(--surface-1); -} - -.nav-searchbox__submit:hover { - background: var(--color-primary-active); -} - -/* ============================================================ - * Mobile drawer / mobile header - * ============================================================ */ -.mobile-header.nav-drawer-backdrop { - position: fixed; - inset: 0; - background: rgba(0, 0, 0, 0.45); - z-index: 1200; - opacity: 0; - visibility: hidden; - transition: opacity 0.3s ease, visibility 0.3s; -} - -.mobile-header.nav-drawer-backdrop.is-open { - opacity: 1; - visibility: visible; -} - -.mobile-header.nav-drawer { - position: fixed; - top: 0; - right: 0; - width: min(100vw, 360px); - height: 100vh; - max-height: 100dvh; - background: var(--surface-page); - z-index: 1210; - box-shadow: var(--shadow-drawer); - transform: translateX(100%); - transition: transform 0.32s var(--ease-out, ease); - display: flex; - flex-direction: column; - overflow: auto; - pointer-events: none; - visibility: hidden; -} - -.mobile-header.nav-drawer.is-open { - transform: translateX(0); - pointer-events: auto; - visibility: visible; -} - -.mobile-navbar { - display: flex; - flex-direction: column; - min-height: 100%; -} - -.mobile-navbar .navbar-brand { - max-width: calc(100% - 52px); - padding: 7px 0; -} - -.mobile-navbar .navbar-logo { - padding: 0; -} - -.nav-drawer-backdrop { - position: fixed; - inset: 0; - background: rgba(0, 0, 0, 0.45); - z-index: 1200; - opacity: 0; - visibility: hidden; - transition: opacity 0.3s ease, visibility 0.3s; -} - -.nav-drawer-backdrop.is-open { - opacity: 1; - visibility: visible; -} - -.nav-drawer { - position: fixed; - top: 0; - right: 0; - width: min(100vw, 360px); - height: 100vh; - max-height: 100dvh; - background: var(--surface-page); - z-index: 1210; - box-shadow: var(--shadow-drawer); - transform: translateX(100%); - transition: transform 0.32s var(--ease-out, ease); - display: flex; - flex-direction: column; - overflow: auto; - pointer-events: none; - visibility: hidden; -} - -.nav-drawer.is-open { - transform: translateX(0); - pointer-events: auto; - visibility: visible; -} - -.nav-drawer__head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; - padding: 16px 20px; - border-bottom: 1px solid var(--color-line); - font-weight: 700; - font-size: 1rem; -} - -.site-brand--drawer { - max-width: calc(100% - 52px); - padding: 7px 0; -} - -.site-brand--drawer .site-brand__media { - padding: 0; -} - -.nav-drawer__search { - padding: 16px 20px 0; -} - -.nav-drawer__search .nav-searchbox__field { - min-height: 40px; -} - -.nav-drawer__search .nav-searchbox__input { - height: 38px; -} - -.nav-drawer__search .nav-searchbox__submit { - height: 28px; - min-width: 58px; - padding: 0 12px; - font-size: 0.75rem; -} - -.nav-searchbox__meta { - display: grid; - gap: 10px; - margin-top: 12px; -} - -.nav-searchbox__types { - display: flex; - flex-wrap: wrap; - gap: 8px; -} - -.nav-searchbox__type { - display: inline-flex; - align-items: center; - min-height: 32px; - padding: 0 12px; - border: 1px solid var(--color-line); - border-radius: var(--radius-pill, 999px); - background: var(--surface-2); - color: var(--color-ink-3); - font-size: 0.8125rem; - font-weight: 600; - cursor: pointer; -} - -.nav-searchbox__type.is-active { - border-color: var(--color-primary); - background: var(--color-primary-soft); - color: var(--color-primary); -} - -.nav-searchbox__category-wrap { - position: relative; -} - -.nav-searchbox__category { - appearance: none; - width: 100%; - min-height: 36px; - padding: 0 34px 0 12px; - border: 1px solid var(--color-line); - border-radius: var(--radius-pill, 999px); - background: var(--surface-2); - color: var(--color-ink); - font: 500 0.875rem/1 var(--font-ui); -} - -.nav-searchbox__category-wrap::after { - content: ''; - position: absolute; - right: 12px; - top: 50%; - transform: translateY(-50%); - width: 0; - height: 0; - border-left: 5px solid transparent; - border-right: 5px solid transparent; - border-top: 5px solid var(--color-ink-3); - pointer-events: none; -} - -.nav-searchbox__category:disabled { - opacity: 0.56; - cursor: not-allowed; -} - -.nav-drawer__menu { - flex: 1; - padding: 16px 20px 32px; -} - -.nav-drawer__menu a { - display: block; - padding: 14px 0; - font-size: 1rem; - font-weight: 500; - color: var(--color-ink); - border-bottom: 1px solid var(--color-line-soft); -} - -.nav-drawer__menu a:hover { - color: var(--color-primary-active); -} - -.nav-drawer__menu ul { - list-style: none; - margin: 0; - padding: 0; -} - -.nav-drawer__auth { - padding: 20px; - display: flex; - flex-direction: column; - gap: 12px; - border-top: 1px solid var(--color-line); -} - -.nav-drawer__auth .btn-outline, -.nav-drawer__auth .btn-primary { - width: 100%; - height: 44px; -} - -.nav-drawer__publish { - padding: 0 20px 20px; -} - -.nav-drawer__publish .btn-primary { - width: 100%; - min-height: 44px; - justify-content: center; - gap: 8px; -} - - - -/* ============================================================ - * Header user box (Zibll-like sub-user-box grid) - * ============================================================ */ -.nav-user__toggle { - position: relative; -} -.nav-user__dot { - position: absolute; - top: -1px; - right: -1px; - width: 8px; - height: 8px; - border-radius: 50%; - background: var(--color-error, #ef4444); - border: 2px solid var(--surface-1, #fff); - box-sizing: content-box; - pointer-events: none; - z-index: 2; -} -.nav-user__guest-avatar { - width: 36px; - height: 36px; - border-radius: 50%; - display: inline-flex; - align-items: center; - justify-content: center; - background: var(--surface-2); - color: var(--color-ink-3); -} -.user-dropdown { - width: 300px; - padding: 14px 12px 12px; -} -.user-dropdown__profile.user-info { - position: relative; - padding-right: 42px; -} -.user-dropdown__meta { - min-width: 0; - flex: 1; -} -.user-dropdown__name { - display: flex; - align-items: center; - gap: 6px; - flex-wrap: wrap; -} -/* Beat generic `.user-dropdown a { width:100%; height:44px }` sizing. */ -.user-dropdown a.user-dropdown__msg, -.user-dropdown__msg { - position: absolute; - top: 8px; - right: 8px; - width: 34px !important; - height: 34px !important; - min-width: 34px; - max-width: 34px; - border-radius: 50%; - display: inline-flex !important; - align-items: center; - justify-content: center; - gap: 0 !important; - padding: 0 !important; - background: var(--surface-2); - color: var(--color-ink-3); - box-sizing: border-box; -} -.user-dropdown a.user-dropdown__msg:hover, -.user-dropdown__msg:hover { - background: var(--surface-3); - color: var(--color-primary); -} -.user-dropdown__msg .user-dropdown__badge { - position: absolute; - top: -4px; - right: -4px; - margin: 0; -} -.user-dropdown__guest-title { - padding: 4px 8px 12px; - text-align: center; - font-weight: 700; - color: var(--color-ink); -} -.user-dropdown__actions { - display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - gap: 8px 4px; - padding: 4px 2px 2px; -} -.user-dropdown__actions--admin { - grid-template-columns: repeat(2, minmax(0, 1fr)); -} -.user-dropdown__action { - display: flex !important; - flex-direction: column; - align-items: center; - justify-content: flex-start; - gap: 6px !important; - width: auto !important; - height: auto !important; - min-height: 0; - padding: 6px 4px !important; - text-align: center !important; - font-size: 12px !important; - line-height: 1.25; - color: var(--color-ink-3) !important; - background: transparent !important; -} -.user-dropdown__action:hover { - background: transparent !important; - color: var(--color-primary) !important; -} -.user-dropdown__action-icon { - width: 40px; - height: 40px; - border-radius: 50%; - display: inline-flex; - align-items: center; - justify-content: center; - font-size: 15px; - color: #fff; -} -.user-dropdown__action-icon.is-blue { background: linear-gradient(135deg, #60a5fa, #3b82f6); } -.user-dropdown__action-icon.is-purple { background: linear-gradient(135deg, #a78bfa, #8b5cf6); } -.user-dropdown__action-icon.is-green { background: linear-gradient(135deg, #34d399, #10b981); } -.user-dropdown__action-icon.is-red { background: linear-gradient(135deg, #f87171, #ef4444); } -.user-dropdown__action-icon.is-yellow { background: linear-gradient(135deg, #fbbf24, #f59e0b); color: #1f2937; } -.user-dropdown__action.is-danger { - color: var(--color-error, #ef4444) !important; -} -.user-dropdown__credits { - margin-top: 2px; - border-radius: 12px; - background: var(--surface-2); - margin-left: 4px; - margin-right: 4px; - width: auto; -} diff --git a/assets/css/frontend/nav/nav-fixed-mobile-fallback.css b/assets/css/frontend/nav/nav-fixed-mobile-fallback.css deleted file mode 100644 index 9490453e..00000000 --- a/assets/css/frontend/nav/nav-fixed-mobile-fallback.css +++ /dev/null @@ -1,10 +0,0 @@ -/* ===== nav_fixed mobile fallback ===== */ -/* On mobile, static nav should remain sticky so the hamburger toggle stays accessible */ -@media (max-width: 767px) { - .has-static-nav .site-nav--static { - position: sticky; - top: 0; - z-index: 1000; - } -} - diff --git a/assets/css/frontend/nav/responsive-mega-menu.css b/assets/css/frontend/nav/responsive-mega-menu.css deleted file mode 100644 index 55a61fe1..00000000 --- a/assets/css/frontend/nav/responsive-mega-menu.css +++ /dev/null @@ -1,109 +0,0 @@ -/* Responsive Mega Menu */ -@media (max-width: 767px) { - ul.nav-desktop .mega-menu-item > .mega-menu__panel { - position: static; - transform: none; - width: 100%; - max-width: none; - margin: 0; - box-shadow: none; - } - - .mega-menu__columns-2, - .mega-menu__columns-3, - .mega-menu__columns-4, - .mega-menu__columns-5 { - grid-template-columns: 1fr; - } -} - -/* ============================================================ - * Right actions (icons + auth) - * ============================================================ */ -.nav-actions { - display: flex; - align-items: center; - gap: 4px; - flex-shrink: 0; - margin-left: auto; - overflow: visible; - position: relative; - z-index: 25; -} - -.nav-settings-link, -.nav-publish-btn { - flex-shrink: 0; -} - -.nav-settings-link svg { - width: 18px; - height: 18px; -} - -.nav-publish-btn { - display: inline-flex; - align-items: center; - gap: 8px; - height: 36px; - padding: 0 14px; - font-size: 0.875rem; - font-weight: 600; -} - -.nav-publish-btn i { - font-size: 0.8125rem; -} - -.theme-toggle.is-animating svg { - transform: rotate(360deg); -} - -/* ============================================================ - * Auth buttons (in nav, when logged out) - * ============================================================ */ -.nav-auth { - display: none; - align-items: center; - gap: 8px; -} - -/* btn-outline / btn-primary 复用 core/buttons.css 的定义, - * 这里仅做导航专用调整(高度对齐、字体) */ -.nav-auth .btn-outline, -.nav-auth .btn-primary { - height: 36px; - font-size: 0.875rem; - padding: 0 16px; -} - -/* Auth dropdown variant (layouts 1/3) — icon trigger + panel */ -.nav-auth-dropdown { - position: relative; - display: none; - align-items: center; -} - -.nav-auth-dropdown__trigger { - width: 36px; - height: 36px; -} - -.nav-auth-dropdown__trigger svg { - width: 20px; - height: 20px; -} - -.nav-auth-dropdown__panel { - display: none; - position: absolute; - top: calc(100% + 8px); - right: 0; - min-width: 180px; - z-index: 1100; -} - -.nav-auth-dropdown.is-open .nav-auth-dropdown__panel { - display: block; -} - diff --git a/assets/css/frontend/nav/zib-utilities.css b/assets/css/frontend/nav/zib-utilities.css new file mode 100644 index 00000000..2f2ed681 --- /dev/null +++ b/assets/css/frontend/nav/zib-utilities.css @@ -0,0 +1,132 @@ +/* Utilities for zibll header chrome */ +.css-icon.i-menu { + position: relative; + display: inline-block; + width: 1.15em; + height: 1em; + vertical-align: -0.1em; +} +.css-icon.i-menu > i, +.css-icon.i-menu::before, +.css-icon.i-menu::after { + content: ""; + position: absolute; + left: 0; + right: 0; + height: 2px; + background: currentColor; + border-radius: 1px; + transition: 0.25s; +} +.css-icon.i-menu > i { top: 50%; margin-top: -1px; } +.css-icon.i-menu::before { top: 0; } +.css-icon.i-menu::after { bottom: 0; } + +.list-inline { padding-left: 0; list-style: none; margin: 0; } +.list-inline > li { display: inline-block; vertical-align: middle; } +.list-inline.splitters > li + li { margin-left: 6px; } + +.navbar-top .flex.ac { display: flex; align-items: center; } +.navbar-top .flex.jsb { display: flex; justify-content: space-between; } +.navbar-top .flex.jsa { display: flex; justify-content: space-around; } +.navbar-top .flex.jc { display: flex; justify-content: center; } +.mb6 { margin-bottom: 6px; } +.mb10 { margin-bottom: 10px; } +.mb20 { margin-bottom: 20px; } +.mt6 { margin-top: 6px; } +.mt10 { margin-top: 10px; } +.ml6 { margin-left: 6px; } +.ml10 { margin-left: 10px; } +.em09 { font-size: 0.9em; } +.em12 { font-size: 1.2em; } +.muted-2-color { opacity: 0.7; } +.muted-3-color { opacity: 0.55; } +.text-ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.padding-10 { padding: 10px; } +.relative { position: relative; } +.inline-block { display: inline-block; } +.hide { display: none !important; } +.pull-right { float: right; } +.null { background: none; border: 0; padding: 0; color: inherit; } +.abs-right { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); } + +/* Mid breakpoint fold trigger (zibll ~991) */ +@media (max-width: 991px) { + .navbar-top .show-nav-but { display: block !important; } + .navbar-top .nav.navbar-nav:not(.show) { + display: none; + } + .navbar-top .nav.navbar-nav.show { + display: block; + position: absolute; + left: 0; right: 0; + top: 100%; + background: var(--main-bg-color, #fff); + z-index: 1001; + padding: 10px; + box-shadow: 0 8px 20px rgba(0,0,0,.08); + } +} + +/* Language dropdown */ +.language-dropdown { position: relative; } +.language-dropdown .dropdown-menu, +.language-dropdown .hover-show-con { + min-width: 140px; + right: 0; + left: auto; + padding: 8px 0; +} +.language-dropdown .translate-button { display: flex; align-items: center; padding: 6px 14px; } +.language-dropdown .translate-button.active { color: var(--focus-color); } + +/* Layout2 inline search focus panel */ +.navbar-collapse .search-input { + min-width: 160px; + max-width: 240px; + height: 34px; + border-radius: 20px; + border: 0; + padding: 0 36px 0 14px; +} +.navbar-form.navbar-left.hover-show { margin: 0 8px; } +.navbar-form .form-group.relative { position: relative; } +.navbar-form .abs-right { right: 10px; } + +/* Mobile drawer body scroll lock */ +body.mobile-navbar-show { overflow: hidden; } + +/* Ensure container-header flex row */ +.container-header { + display: flex; + align-items: center; + flex-wrap: nowrap; + max-width: var(--nav-max-width, 100%); + margin: 0 auto; + width: 100%; +} +.navbar-collapse { + display: flex; + align-items: center; + flex: 1; + min-width: 0; + flex-wrap: nowrap; +} +.navbar-header { display: flex; align-items: center; flex-shrink: 0; } +.navbar-top .navbar-toggle { display: none; background: none; border: 0; color: inherit; } +@media (max-width: 767px) { + .navbar-top .navbar-toggle { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; } + .navbar-collapse { display: none; } +} + +/* Senior submenu full width under fixed header */ +.navbar-top .sub-menu.senior-submenu { + position: fixed; + left: 0; right: 0; + top: 59px; + width: 100%; + min-width: 100%; + padding: 20px 0; + border-radius: 0; + z-index: 998; +} diff --git a/assets/js/core/nav.js b/assets/js/core/nav.js index 86c3d09a..b773f568 100644 --- a/assets/js/core/nav.js +++ b/assets/js/core/nav.js @@ -1,13 +1,15 @@ /** - * AKSRT nav chrome — zibll contract (body.mobile-navbar-show, folding, chevron). - * Drawer open/close is handled by aksrt-runtime [data-toggle-class]. + * AKSRT nav chrome — zibll contract (folding, chevron, language, drawer a11y). + * Drawer open/close via aksrt-runtime [data-toggle-class] → body.mobile-navbar-show. */ (function () { "use strict"; - function appendChevrons() { - document.querySelectorAll(".navbar-top li.menu-item-has-children > a").forEach(function (a) { - if (a.querySelector(".fa-angle-down")) return; + var foldTimer = null; + + function appendChevrons(root) { + (root || document).querySelectorAll(".navbar-top li.menu-item-has-children > a").forEach(function (a) { + if (a.querySelector(".fa-angle-down, .fas.fa-angle-down")) return; var i = document.createElement("i"); i.className = "fa fa-angle-down ml6"; i.setAttribute("aria-hidden", "true"); @@ -15,18 +17,30 @@ }); } + function clearFolding() { + var more = document.querySelector(".navbar-nav > li.aksrt-nav-more"); + if (!more) return; + var sub = more.querySelector(".sub-menu"); + var nav = more.parentNode; + if (sub && nav) { + while (sub.firstChild) { + nav.insertBefore(sub.firstChild, more); + } + } + more.remove(); + } + function navFolding() { var collapse = document.querySelector(".navbar-collapse"); var nav = document.querySelector(".navbar-nav"); if (!collapse || !nav) return; - if (window.innerWidth <= 996) return; + clearFolding(); + + if (window.innerWidth <= 996) return; var header = document.querySelector(".header"); if (header && header.offsetHeight <= 81) return; - // Already folded? - if (nav.querySelector("li.aksrt-nav-more")) return; - var surplus = collapse.clientWidth; Array.prototype.forEach.call(collapse.children, function (child) { if (!child.classList.contains("navbar-nav")) { @@ -40,9 +54,7 @@ var foldItems = []; Array.prototype.forEach.call(nav.children, function (li) { navsWidth += li.offsetWidth || 0; - if (navsWidth > surplus) { - foldItems.push(li); - } + if (navsWidth > surplus) foldItems.push(li); }); if (!foldItems.length) return; @@ -51,14 +63,33 @@ more.innerHTML = ''; var sub = more.querySelector(".sub-menu"); - foldItems.forEach(function (li) { - sub.appendChild(li); - }); + foldItems.forEach(function (li) { sub.appendChild(li); }); nav.appendChild(more); + appendChevrons(more); + } + + function scheduleFold() { + if (foldTimer) clearTimeout(foldTimer); + foldTimer = setTimeout(function () { + try { navFolding(); } catch (e) {} + }, 80); + } + + function bindLanguage() { + document.addEventListener("click", function (e) { + var btn = e.target && e.target.closest ? e.target.closest(".translate-button[data-language]") : null; + if (!btn) return; + e.preventDefault(); + var lang = btn.getAttribute("data-language"); + if (!lang) return; + try { localStorage.setItem("aksrt_lang", lang); } catch (err) {} + var url = new URL(window.location.href); + url.searchParams.set("lang", lang); + window.location.href = url.toString(); + }); } function bindDrawerA11y() { - // Esc closes mobile nav via body class. document.addEventListener("keydown", function (e) { if (e.key === "Escape" && document.body.classList.contains("mobile-navbar-show")) { document.body.classList.remove("mobile-navbar-show"); @@ -68,18 +99,14 @@ function init() { appendChevrons(); - try { - navFolding(); - } catch (err) { - // ignore fold failures - } + scheduleFold(); + bindLanguage(); bindDrawerA11y(); + window.addEventListener("resize", scheduleFold, { passive: true }); } if (window.AksrtFrontend && typeof window.AksrtFrontend.register === "function") { - window.AksrtFrontend.register("aksrt-nav-chrome", function () { - init(); - }); + window.AksrtFrontend.register("aksrt-nav-chrome", function () { init(); }); } else if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", init); } else { diff --git a/assets/js/section-navs.js b/assets/js/section-navs.js index ad70ed9f..d34e283a 100644 --- a/assets/js/section-navs.js +++ b/assets/js/section-navs.js @@ -1,9 +1,7 @@ /** - * AKSRT Section Navs — Article TOC / Scrollspy + * AKSRT Section Navs — zibll section_navs parity (posts-nav-lists + scrollspy). * - * Auto-generates a table of contents from h1~h4 headings within - * [data-nav] content areas. Scrollspy highlights the current section. - * Collapsible sub-items when the TOC is tall. + * Builds TOC from [data-nav] h1–h4 into .posts-nav-box / .posts-nav-lists. * * @package AKSRT */ @@ -12,164 +10,138 @@ var HEADING_SEL = 'h1, h2, h3, h4'; var MIN_HEADINGS = 2; - var SCROLL_OFFSET = 105; - var MAX_HEIGHT_COLLAPSE = 380; + var NAVBOX = '.posts-nav-box'; + var NAV_CLASS = 'posts-nav-lists'; - function initSectionNavs() { - var navContainer = document.querySelector('[data-nav]'); - if (!navContainer) return; + function isExcluded(el) { + return el.classList.contains('item-heading') || !!el.closest('.no-nav'); + } - // Single-post TOC is owned by page-template.js (.posts-nav-box .toc-list). - // Do not invent a second .aksrt-section-nav tree on article singles. - if (document.querySelector('.posts-nav-box .toc-list, .posts-nav-box.toc-widget')) { + function ensureId(el, index) { + if (!el.id) { + el.id = 'section-nav-' + index; + } + return el.id; + } + + function buildLists(root) { + var headings = root.querySelectorAll(HEADING_SEL); + var html = ''; + var count = 0; + headings.forEach(function (h, i) { + if (isExcluded(h)) return; + var id = ensureId(h, i); + var tag = h.tagName.toUpperCase(); + var text = (h.textContent || '').trim(); + if (!text) return; + html += '
  • ' + text + '
  • '; + count++; + }); + return count >= MIN_HEADINGS ? html : ''; + } + + function fillBoxes(listsHtml) { + document.querySelectorAll(NAVBOX).forEach(function (box) { + var existing = box.querySelector('.' + NAV_CLASS); + if (existing) { + existing.innerHTML = listsHtml; + return; + } + var title = box.getAttribute('data-title') || ''; + var titleHtml = title + ? '
    ' + title + '
    ' + : ''; + box.insertAdjacentHTML( + 'beforeend', + titleHtml + + '
    ' + ); + }); + } + + function smoothTo(hash) { + var el = document.querySelector(hash); + if (!el) return; + var offset = 0; + var header = document.querySelector('.header'); + if (document.body.classList.contains('nav-fixed') && header) { + offset = header.offsetHeight + 12; + } + var top = el.getBoundingClientRect().top + window.pageYOffset - offset; + window.scrollTo({ top: top, behavior: 'smooth' }); + } + + function bindClicks() { + document.addEventListener('click', function (e) { + var a = e.target && e.target.closest ? e.target.closest('.' + NAV_CLASS + ' a[href^="#"]') : null; + if (!a) return; + var href = a.getAttribute('href'); + if (!href || href.length < 2) return; + e.preventDefault(); + history.replaceState(null, '', href); + smoothTo(href); + }); + } + + function scrollspy() { + var links = document.querySelectorAll('.' + NAV_CLASS + ' a[href^="#"]'); + if (!links.length) return; + var map = []; + links.forEach(function (a) { + var id = a.getAttribute('href').slice(1); + var el = document.getElementById(id); + if (el) map.push({ el: el, a: a, li: a.parentElement }); + }); + function update() { + var y = window.scrollY + 110; + var current = null; + for (var i = 0; i < map.length; i++) { + if (map[i].el.offsetTop <= y) current = map[i]; + } + map.forEach(function (m) { + if (m.li) m.li.classList.toggle('active', m === current); + }); + } + window.addEventListener('scroll', update, { passive: true }); + update(); + } + + function autoHash() { + if (window.location.hash) { + setTimeout(function () { + smoothTo(window.location.hash); + }, 40); + } + } + + function init() { + if (document.querySelector('.posts-nav-box .toc-list, .posts-nav-box.toc-widget .toc-list')) { return; } - - var headings = navContainer.querySelectorAll(HEADING_SEL); - if (headings.length < MIN_HEADINGS) return; - - var tocContainer = document.querySelector('.posts-nav-lists, .aksrt-section-nav'); - if (!tocContainer) { - // Docs / page-navs only: create a dedicated container. Never inject into single sidebars. - if (document.body.classList.contains('single') || document.body.classList.contains('single-post')) { - return; - } - tocContainer = document.createElement('nav'); - tocContainer.className = 'aksrt-section-nav'; - tocContainer.setAttribute('aria-label', '文章目录'); - var sidebar = document.querySelector('.content-layout__aside, .sidebar, .widget-area'); - if (sidebar) { - sidebar.insertBefore(tocContainer, sidebar.firstChild); - } else if (navContainer.parentNode) { - navContainer.parentNode.insertBefore(tocContainer, navContainer); - } else { - return; - } + var root = document.querySelector('[data-nav], .article-content, .entry-content, main.article'); + if (!root) return; + var listsHtml = buildLists(root); + if (!listsHtml) return; + if (!document.querySelector(NAVBOX) && !document.querySelector('.' + NAV_CLASS)) { + return; } + fillBoxes(listsHtml); + bindClicks(); + scrollspy(); + autoHash(); + } - var list = document.createElement('ul'); - list.className = 'aksrt-section-nav__list'; - - var items = []; - headings.forEach(function (heading, i) { - if (!heading.id) { - heading.id = 'section-nav-' + i; - } - - var level = parseInt(heading.tagName.charAt(1), 10); - var li = document.createElement('li'); - li.className = 'aksrt-section-nav__item aksrt-section-nav__item--h' + level; - - var link = document.createElement('a'); - link.href = '#' + heading.id; - link.textContent = heading.textContent.trim(); - link.className = 'aksrt-section-nav__link'; - - link.addEventListener('click', function (e) { - e.preventDefault(); - smoothScrollTo(heading); - - // Expand collapsed panels containing the target - var panel = heading.closest('[data-collapse], .collapse'); - if (panel) { - panel.classList.add('show', 'is-open'); - var readMore = panel.previousElementSibling; - if (readMore && readMore.classList.contains('read-more-open')) readMore.click(); - } - }); - - li.appendChild(link); - list.appendChild(li); - items.push({ el: heading, link: link, li: li, level: level }); + if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') { + window.AksrtFrontend.register('aksrt-section-navs', function () { + init(); }); - - tocContainer.innerHTML = ''; - tocContainer.appendChild(list); - - // Collapse sub-items if TOC is tall - if (tocContainer.scrollHeight > MAX_HEIGHT_COLLAPSE) { - collapseSubItems(list, items); - } - - // Scrollspy - var ticking = false; - function onScroll() { - if (ticking) return; - ticking = true; - requestAnimationFrame(function () { - ticking = false; - var scrollTop = window.pageYOffset + SCROLL_OFFSET; - var activeIdx = 0; - for (var i = items.length - 1; i >= 0; i--) { - if (items[i].el.offsetTop <= scrollTop) { - activeIdx = i; - break; - } - } - items.forEach(function (item, idx) { - item.link.classList.toggle('is-active', idx === activeIdx); - }); - }); - } - - window.addEventListener('scroll', onScroll, { passive: true }); - onScroll(); - - // URL hash auto-scroll - if (window.location.hash) { - var target = document.querySelector(window.location.hash); - if (target) setTimeout(function () { smoothScrollTo(target); }, 100); - } - } - - function smoothScrollTo(el) { - if (window.aksrt && aksrt.scrollTopTo) { - aksrt.scrollTopTo(el); - } else { - var top = el.getBoundingClientRect().top + window.pageYOffset - SCROLL_OFFSET; - window.scrollTo({ top: Math.max(0, top), behavior: 'smooth' }); - } - } - - function collapseSubItems(list, items) { - var parentStack = []; - items.forEach(function (item) { - while (parentStack.length && parentStack[parentStack.length - 1].level >= item.level) { - parentStack.pop(); - } - - if (item.level >= 3 && parentStack.length) { - item.li.classList.add('yc'); - // Add collapse toggle to parent if not already there - var parentLi = parentStack[parentStack.length - 1].li; - if (!parentLi.querySelector('.nav-toggle-collapse')) { - var toggle = document.createElement('span'); - toggle.className = 'nav-toggle-collapse'; - toggle.innerHTML = ''; - toggle.addEventListener('click', function (e) { - e.stopPropagation(); - toggle.classList.toggle('fa-rotate-90'); - var next = parentLi.nextElementSibling; - while (next && next.classList.contains('yc')) { - next.classList.toggle('yc-open'); - next = next.nextElementSibling; - } - }); - parentLi.appendChild(toggle); - } - } - - parentStack.push(item); - }); - } - - if (window.AksrtFrontend) { - AksrtFrontend.register('aksrt-section-navs', function () { initSectionNavs(); }); - } - - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initSectionNavs); + } else if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); } else { - initSectionNavs(); + init(); } })(); diff --git a/inc/helpers/aksrt-i18n-switch.php b/inc/helpers/aksrt-i18n-switch.php index 3a5844aa..d9b56bb7 100644 --- a/inc/helpers/aksrt-i18n-switch.php +++ b/inc/helpers/aksrt-i18n-switch.php @@ -147,3 +147,56 @@ function aksrt_render_language_switcher(string $style = 'dropdown'): string { return $html; } + +/** + * Header language dropdown (zibll zib_get_translate_language_btn). + * Uses SPA i18n_available_langs when i18n enabled and position includes header. + * + * @param string $class Extra classes. + */ +function aksrt_get_translate_language_btn(string $class = ''): string { + if (!class_exists('\AKSRT\Settings\Settings')) { + return ''; + } + $cfg = \AKSRT\Settings\Settings::get_i18n_config(); + if (empty($cfg['enabled'])) { + return ''; + } + $pos = (string) ($cfg['switcherPosition'] ?? 'footer'); + if (!in_array($pos, ['header', 'both'], true)) { + return ''; + } + + $raw = (string) ($cfg['availableLangs'] ?? ''); + $buttons = []; + foreach (preg_split('/ +| | +/', $raw) as $line) { + $line = trim($line); + if ($line === '' || strpos($line, '|') === false) { + continue; + } + [$code, $label] = array_map('trim', explode('|', $line, 2)); + if ($code === '' || $label === '') { + continue; + } + $buttons[] = ['language' => $code, 'text' => $label]; + } + if (!$buttons) { + return ''; + } + + $buttons_html = ''; + foreach ($buttons as $button) { + $buttons_html .= '
  • ' . esc_html($button['text']) . '
  • '; + } + $is_mobile = wp_is_mobile(); + $dropdown = ''; + $hover = !$is_mobile ? ' hover-show' : ''; + $toggle = !$is_mobile ? '' : ' data-toggle="dropdown"'; + return ''; +} + diff --git a/inc/helpers/header/aksrt-header-mobile.php b/inc/helpers/header/aksrt-header-mobile.php index 9981e3d7..a5d5629e 100644 --- a/inc/helpers/header/aksrt-header-mobile.php +++ b/inc/helpers/header/aksrt-header-mobile.php @@ -80,6 +80,9 @@ if (!function_exists('aksrt_nav_mobile_content')) : $top_btns .= ''; } + if (function_exists('aksrt_get_translate_language_btn')) { + $top_btns .= aksrt_get_translate_language_btn('pull-right'); + } if ($top_btns !== '') { echo '
    ' . $top_btns . '
    '; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } @@ -116,6 +119,10 @@ if (!function_exists('aksrt_nav_mobile_content')) : } echo $menu; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + if (is_singular('post')) { + echo '
    '; + } + // User box at bottom of drawer. if (!empty($ctx['user_menu'])) { echo aksrt_header_user_box_html($ctx); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped diff --git a/inc/helpers/header/aksrt-header-navbar.php b/inc/helpers/header/aksrt-header-navbar.php index 8ebbf371..68188451 100644 --- a/inc/helpers/header/aksrt-header-navbar.php +++ b/inc/helpers/header/aksrt-header-navbar.php @@ -104,6 +104,7 @@ if (!function_exists('aksrt_get_menu_search')) : $type = (string) ($state['type'] ?? 'post'); $cat_enabled = !empty($state['category_enabled']); $cat_id = (int) ($state['category_id'] ?? 0); + $hots = is_array($ctx['search_hot_keywords'] ?? null) ? $ctx['search_hot_keywords'] : []; echo ''; } endif; @@ -179,13 +193,25 @@ if (!function_exists('aksrt_header_user_box_html')) : } if (!empty($ctx['can_publish'])) { - $pub_text = class_exists('\AKSRT\Settings\Settings') - ? (string) (\AKSRT\Settings\Settings::get_all()['header_user_newposts_text'] ?? '') - : ''; + $all = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : []; + $pub_text = (string) ($all['header_user_newposts_text'] ?? ''); if ($pub_text === '') { $pub_text = __('发布内容', 'aksrt'); } - $href .= ''; + $pub_type = sanitize_key((string) ($all['header_user_newposts_btn'] ?? 'post')); + $pub_url = (string) $ctx['publish_url']; + if ($pub_type === 'topic' && function_exists('aksrt_get_forum_compose_url')) { + $u = aksrt_get_forum_compose_url(); + if ($u) { $pub_url = $u; } + } elseif ($pub_type === 'product') { + if (function_exists('aksrt_get_product_edit_url')) { + $u = (string) aksrt_get_product_edit_url(); + if ($u !== '') { $pub_url = $u; } + } else { + $pub_url = admin_url('post-new.php?post_type=aksrt_product'); + } + } + $href .= ''; $href .= '
    '; $href .= '
    ' . esc_html($pub_text) . '
    '; } @@ -284,6 +310,9 @@ if (!function_exists('aksrt_menu_button')) : $radius_but .= ''; $radius_but .= ''; } + if (function_exists('aksrt_get_translate_language_btn')) { + $radius_but .= aksrt_get_translate_language_btn('ml10 pull-right'); + } if ($radius_but !== '') { $button .= ''; } diff --git a/src/AKSRT/Admin/NavMenuOptions.php b/src/AKSRT/Admin/NavMenuOptions.php index 51900b3e..9821a877 100644 --- a/src/AKSRT/Admin/NavMenuOptions.php +++ b/src/AKSRT/Admin/NavMenuOptions.php @@ -1,6 +1,6 @@

    @@ -71,8 +77,19 @@ class NavMenuOptions {

    -

    - +

    + +

    +

    +

    sanitize_text_field((string) ($raw['icon'] ?? '')), @@ -100,13 +114,138 @@ class NavMenuOptions { 'submenu_s' => !empty($raw['submenu_s']), 'submenu_type' => sanitize_key((string) ($raw['submenu_type'] ?? '')), ]; - // Preserve complex opts not edited by this form. - if (!empty($existing['graphic_card_opts'])) { - $clean['graphic_card_opts'] = $existing['graphic_card_opts']; + + $graphic = self::decode_json_field((string) ($raw['graphic_card_opts_json'] ?? '')); + if (is_array($graphic)) { + $clean['graphic_card_opts'] = self::sanitize_graphic_opts($graphic); } - if (!empty($existing['multi_column_links_opts'])) { - $clean['multi_column_links_opts'] = $existing['multi_column_links_opts']; + $multi = self::decode_json_field((string) ($raw['multi_column_links_opts_json'] ?? '')); + if (is_array($multi)) { + $clean['multi_column_links_opts'] = self::sanitize_multi_opts($multi); } + update_post_meta($menu_item_db_id, 'aksrt_menu_options', $clean); } + + /** + * @return array|null + */ + private static function decode_json_field(string $json): ?array { + $json = trim($json); + if ($json === '') { + return null; + } + $data = json_decode($json, true); + return is_array($data) ? $data : null; + } + + /** + * @param array $opts + * @return array + */ + private static function sanitize_graphic_opts(array $opts): array { + $items_in = is_array($opts['items'] ?? null) ? $opts['items'] : []; + $items = []; + foreach ($items_in as $item) { + if (!is_array($item)) { + continue; + } + $items[] = [ + 'img' => esc_url_raw((string) ($item['img'] ?? '')), + 'title' => sanitize_text_field((string) ($item['title'] ?? '')), + 'badge' => sanitize_text_field((string) ($item['badge'] ?? '')), + 'badge_class' => sanitize_html_class((string) ($item['badge_class'] ?? '')), + 'link' => [ + 'url' => esc_url_raw((string) ($item['link']['url'] ?? '')), + 'text' => sanitize_text_field((string) ($item['link']['text'] ?? '')), + 'target' => sanitize_text_field((string) ($item['link']['target'] ?? '')), + ], + ]; + } + return [ + 'arrangement' => sanitize_key((string) ($opts['arrangement'] ?? '')), + 'align' => sanitize_key((string) ($opts['align'] ?? '')), + 'size' => sanitize_key((string) ($opts['size'] ?? '')), + 'img_scale' => max(0, min(200, (int) ($opts['img_scale'] ?? 100))), + 'items' => $items, + ]; + } + + /** + * @param array $opts + * @return array + */ + private static function sanitize_multi_opts(array $opts): array { + $columns_in = is_array($opts['columns'] ?? null) ? $opts['columns'] : []; + $columns = []; + foreach ($columns_in as $col) { + if (!is_array($col)) { + continue; + } + $items = []; + if (!empty($col['items']) && is_array($col['items'])) { + foreach ($col['items'] as $item) { + if (!is_array($item)) { + continue; + } + $items[] = [ + 'title' => sanitize_text_field((string) ($item['title'] ?? '')), + 'icon' => sanitize_text_field((string) ($item['icon'] ?? '')), + 'img_icon' => esc_url_raw((string) ($item['img_icon'] ?? '')), + 'link' => [ + 'url' => esc_url_raw((string) ($item['link']['url'] ?? '')), + 'text' => sanitize_text_field((string) ($item['link']['text'] ?? '')), + 'target' => sanitize_text_field((string) ($item['link']['target'] ?? '')), + ], + ]; + } + } + $columns[] = [ + 'title' => sanitize_text_field((string) ($col['title'] ?? '')), + 'icon' => sanitize_text_field((string) ($col['icon'] ?? '')), + 'img_icon' => esc_url_raw((string) ($col['img_icon'] ?? '')), + 'link' => [ + 'url' => esc_url_raw((string) ($col['link']['url'] ?? '')), + 'text' => sanitize_text_field((string) ($col['link']['text'] ?? '')), + 'target' => sanitize_text_field((string) ($col['link']['target'] ?? '')), + ], + 'items' => $items, + ]; + } + return ['columns' => $columns]; + } + + private static function sample_graphic_json(): string { + $sample = [ + 'arrangement' => '', + 'size' => 'md', + 'img_scale' => 100, + 'items' => [ + [ + 'img' => '', + 'title' => '卡片标题', + 'badge' => '', + 'badge_class' => 'jb-red', + 'link' => ['url' => home_url('/'), 'text' => '', 'target' => ''], + ], + ], + ]; + return (string) wp_json_encode($sample, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); + } + + private static function sample_multi_json(): string { + $sample = [ + 'columns' => [ + [ + 'title' => '栏目一', + 'icon' => 'fas fa-folder', + 'link' => ['url' => '', 'text' => '', 'target' => ''], + 'items' => [ + ['title' => '链接 A', 'icon' => '', 'link' => ['url' => home_url('/'), 'text' => '', 'target' => '']], + ], + ], + ], + ]; + return (string) wp_json_encode($sample, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); + } } diff --git a/src/AKSRT/Admin/Schema/SchemaPageDisplay.php b/src/AKSRT/Admin/Schema/SchemaPageDisplay.php index 492ece41..c84290d1 100644 --- a/src/AKSRT/Admin/Schema/SchemaPageDisplay.php +++ b/src/AKSRT/Admin/Schema/SchemaPageDisplay.php @@ -64,6 +64,11 @@ class SchemaPageDisplay { ['label' => '帖子', 'value' => 'topic'], ['label' => '商品', 'value' => 'product'], ]], + ['key' => 'header_user_newposts_btn', 'label' => '用户卡发布类型', 'type' => 'select', 'default' => 'post', 'options' => [ + ['label' => '文章', 'value' => 'post'], + ['label' => '帖子', 'value' => 'topic'], + ['label' => '商品', 'value' => 'product'], + ]], ['key' => 'header_user_newposts_text', 'label' => '用户卡发布文案', 'type' => 'input', 'default' => '发布内容'], ]], ['key' => 'color', 'title' => '颜色设置', 'fields' => [ diff --git a/src/AKSRT/Core/ScriptsPart1b.php b/src/AKSRT/Core/ScriptsPart1b.php index 1878ff11..8c546af0 100644 --- a/src/AKSRT/Core/ScriptsPart1b.php +++ b/src/AKSRT/Core/ScriptsPart1b.php @@ -475,6 +475,17 @@ trait ScriptsPart1b { // 文章导航页(zibll postsnavs.php) if (is_page_template('templates/pages/page-posts-navs.php')) { + $page_navs_js = get_template_directory() . '/assets/js/page-navs.js'; + if (file_exists($page_navs_js)) { + wp_enqueue_script( + 'aksrt-page-navs', + $base_uri . '/assets/js/page-navs.js', + [], + (string) filemtime($page_navs_js), + true + ); + } + $pn_css = get_template_directory() . '/assets/css/routes/posts-navs.css'; if (file_exists($pn_css)) { wp_enqueue_style( From e3848ddf8ce7319664be5685f33a7f929e29a2d6 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 22 Jul 2026 11:42:13 +0800 Subject: [PATCH 2/3] feat(nav): paint header theme colors via InlineStyleBuilder Apply SPA header_theme_* as --header-bg/--header-color on .header when header_theme_custom is enabled (zibll header_theme_custom parity). --- src/AKSRT/Frontend/InlineStyleBuilder.php | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/AKSRT/Frontend/InlineStyleBuilder.php b/src/AKSRT/Frontend/InlineStyleBuilder.php index cb29d2ae..dd812f0d 100644 --- a/src/AKSRT/Frontend/InlineStyleBuilder.php +++ b/src/AKSRT/Frontend/InlineStyleBuilder.php @@ -105,8 +105,28 @@ class InlineStyleBuilder { echo '--focus-color:' . esc_attr($dark_primary) . ';'; } echo '}' . "\n"; + + // Zibll header_theme_custom → --header-bg / --header-color on .header. + if (class_exists('\AKSRT\Settings\Settings')) { + $ht = \AKSRT\Settings\Settings::get_header_theme_config(); + if (!empty($ht['custom'])) { + $h_bg = $ht['bg_custom'] !== '' ? $ht['bg_custom'] : ($ht['bg'] ?? ''); + $h_fg = $ht['color_custom'] ?? ''; + if ($h_bg !== '' || $h_fg !== '') { + echo '' . "\n"; + } + } + } + if (class_exists('\AKSRT\Settings\Settings') && !empty(\AKSRT\Settings\Settings::get_value('close_backdrop', false))) { - echo '' . "\n"; + echo '' . "\n"; } echo '