diff --git a/assets/admin/src/schema/sections/page-display/index.js b/assets/admin/src/schema/sections/page-display/index.js index 8eb1c048..8b2f05aa 100644 --- a/assets/admin/src/schema/sections/page-display/index.js +++ b/assets/admin/src/schema/sections/page-display/index.js @@ -32,8 +32,12 @@ export const pageDisplaySchemas = { header_mobile_drawer: true, header_nav_style: 'default', header_max_width: 1200, - header_scroll_down_hide: false, + header_scroll_down_hide: true, header_layout: '1', + header_nav_pay_vip: true, + header_nav_user_pay_vip: true, + header_newposts_types: ['post'], + header_user_newposts_text: '发布内容', mobile_header_layout: 'center', mobile_navbar_align: 'left', header_newposts_enabled: true, @@ -63,7 +67,7 @@ export const pageDisplaySchemas = { { label: '1200px', value: 1200 }, { label: '1320px', value: 1320 }, ]}, - { key: 'header_scroll_down_hide', label: '下滑隐藏导航', type: 'switch', default: false, help: '页面向下滑动时自动隐藏导航栏' }, + { key: 'header_scroll_down_hide', label: '下滑隐藏导航', type: 'switch', default: true, help: '下滚动隐藏,上滚动显示(需开启固定导航)' }, ], }, { @@ -115,6 +119,14 @@ export const pageDisplaySchemas = { { key: 'header_newposts_text', label: '按钮文案', type: 'input', default: ' 发布', dependsOn: { key: 'header_newposts_enabled', value: true } }, { key: 'header_newposts_radius', label: '圆角 (px)', type: 'number', default: 20, min: 0, max: 30, dependsOn: { key: 'header_newposts_enabled', value: true } }, { key: 'header_newposts_class', label: '自定义 CSS 类', type: 'input', default: '', dependsOn: { key: 'header_newposts_enabled', value: true } }, + { key: 'header_newposts_types', label: '发布类型', type: 'select', multiple: true, default: ['post'], options: [ + { label: '文章', value: 'post' }, + { label: '帖子', value: 'topic' }, + { label: '商品', value: 'product' }, + ], dependsOn: { key: 'header_newposts_enabled', value: true }, help: '未开启的模块不会显示' }, + { 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 01861f5b..10fd9882 100644 --- a/assets/css/frontend/nav.css +++ b/assets/css/frontend/nav.css @@ -1,12 +1,11 @@ -@import url("nav/base.css"); +@import url("nav/zib-navbar-fixed.css"); +@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/responsive-mega-menu.css"); -@import url("nav/mobile-toggle.css"); -@import url("nav/mobile-theme-toggle-in-drawer.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/mega-submenu.css"); -@import url("nav/cards-submenu.css"); -@import url("nav/nav-fixed-mobile-fallback.css"); diff --git a/assets/css/frontend/nav/zib-mobile-navbar.css b/assets/css/frontend/nav/zib-mobile-navbar.css new file mode 100644 index 00000000..8c94c867 --- /dev/null +++ b/assets/css/frontend/nav/zib-mobile-navbar.css @@ -0,0 +1,195 @@ +/* Ported/adapted from zibll main.css — mobile navbar */ +@media (max-width: 767px) { + opacity: 0.7; + height: 36px; + } + + .navbar-toggle .icon-bar { + background: var(--main-color); + } + + .navbar-toggle .icon-bar + .icon-bar { + margin-top: 5px; + } + + .navbar-top.center .navbar-brand { + position: absolute; + width: 100%; + text-align: center; + } + + .navbar-top.center .navbar-brand + .navbar-toggle { + float: left; + } + + .navbar-top.center .navbar-brand > img { + display: unset; + } + + .navbar-top .navbar-logo > img { + padding: 0; + } + + .mobile-navbar { + z-index: 997; + position: fixed; + top: 0; + bottom: 0; + background: var(--main-bg-color); + width: 281px; + padding: 10px 20px 70px; + transition: transform 0.5s; + } + + .mobile-navbar .navbar-logo { + margin-left: -10px; + } + + .mobile-navbar badge:not(.top) { + transform: unset; + margin-left: 5px; + } + + .mobile-navbar.right { + right: -2px; + transform: translateX(110%); + } + + .mobile-navbar.left { + left: -2px; + transform: translateX(-110%); + } + + .mobile-navbar.left > .toggle-radius { + float: right; + } + + .mobile-navbar.top { + height: auto; + max-height: 70%; + width: 100%; + transform: translateY(-110%); + } + + .mobile-navbar-show .mobile-navbar { + transition: transform 0.6s cubic-bezier(0.28, 0.9, 0.34, 0.99); + transform: translate(0); + } + + .mobile-navbar-show .mobile-navbar + .fixed-body { + visibility: unset; + opacity: 1; + } + + .mobile-navbar-show .navbar-toggle .i-menu > i { + opacity: 0; + } + + .mobile-navbar-show .navbar-toggle .i-menu::after { + transform: rotate(45deg); + width: calc(1em + 1px); + transform-origin: bottom right; + } + .mobile-navbar-show .navbar-toggle .i-menu::before { + right: 0; + width: calc(1em + 1px); + transform: rotate(-45deg); + transform-origin: top right; + } + + .mobile-navbar .navbar-header { + margin-top: -58px; + margin-bottom: 10px; + } + + .mobile-navbar .navbar-brand { + padding: 7px 10px; + } + + .mobile-navbar .toggle-radius { + margin: 0 2px; + } + + .mobile-menus li a { + display: block; + padding: 6px 0; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + .mobile-menus > li > a::before { + margin: 0; + margin-right: 12px; + } + + .mobile-menus .sub-menu { + word-wrap: break-word; + white-space: normal; + margin: 0 -2px; + } + + .mobile-menus .sub-menu li { + width: calc(50% - 4px); + text-align: center; + display: inline-block; + } + + .mobile-navbar.top .mobile-menus .sub-menu li { + width: calc(33.3% - 4px); + } + + .mobile-menus .sub-menu li > a { + background: var(--main-border-color); + padding: 5px; + margin: 2px; + border-radius: 4px; + font-size: 0.9em; + position: relative; + } + + .mobile-menus .sub-menu li > a > badge:not(.top) { + position: absolute; + right: 0; + top: 0; + transform: scale(0.85); + } + + .mobile-user-menus a { + width: calc(50% - 12px); + float: left; + margin: 6px; + } + + .mobile-nav-widget { + display: block; + width: 100%; + overflow: hidden; + white-space: normal; + } + + .mobile-navbar .box-body.notop { + padding-left: 0; + padding-right: 0; + } + + .mobile-navbar .sub-user-box { + margin-bottom: 30px; + } + + .mobile-navbar .posts-nav-box .zib-widget { + background: var(--muted-border-color); + box-shadow: none; + } + + .s-desc, + .slide-text, + .wp-block-carousel figcaption { + padding: 0.7em; + } + + .posts-item .item-thumbnail { + width: 138px; + } + +} diff --git a/assets/css/frontend/nav/zib-navbar-core.css b/assets/css/frontend/nav/zib-navbar-core.css new file mode 100644 index 00000000..7c5781f0 --- /dev/null +++ b/assets/css/frontend/nav/zib-navbar-core.css @@ -0,0 +1,777 @@ +/* Ported/adapted from zibll main.css — top navbar core */ + +.main-shadow { + transition: box-shadow 0.3s; +} + +.but:focus, +.but:hover, +.progress, +.navbar-nav > li:before { + transition: 0.3s; +} + +.navbar-top .sub-menu, +.theme-popover, +.hover-show .hover-show-con { + opacity: 0; + transform: translateY(6px); + transition: 0.4s; +} + +.navbar-top li:hover > .sub-menu, +.hover-show:hover .hover-show-con, +.hover-show:hover ~ .hover-show-con, +.hover-show:hover ~ .sub-menu { + opacity: 1; + transform: translateY(0); +} + +.hover-show { + position: relative; + cursor: pointer; + z-index: 10; +} + +.hover-show .hover-show-con { + position: absolute; + visibility: hidden; +} + +.hover-show:hover .hover-show-con, +.hover-show:hover ~ .hover-show-con, +.hover-show:hover ~ .sub-menu { + visibility: unset; +} + +.match-ok, +.navbar-top .sub-menu li > a, +.ic-close { + transition: 0.3s; +} + + + margin-left: -100%; +} + +.header { + padding: 8px 20px; + background: var(--header-bg); + margin-bottom: 20px; + transition: 0.15s; +} + +.body-scroll .header { + box-shadow: 0px 4px 10px var(--main-shadow); +} + +.header a:not(.but), +.header svg { + color: var(--header-color); +} + +.header .sub-menu { + background: var(--main-bg-color); + text-shadow: none; +} + +.header .sub-menu a, +.header .dropdown-menu a, +.header .sub-menu svg { + color: var(--main-color); +} + +[data-language].active, +.header [data-language].active { + color: var(--focus-color); +} + +.header .language-dropdown .dropdown-menu { + right: -20px; +} + +.header .language-dropdown.pull-right.dropdown .dropdown-menu::before { + right: 30px; +} + +.language-dropdown .loading-backdrop { + border-radius: 4px; + background: rgba(0, 0, 0, 0.1); +} + +body.nav-fixed: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.1) 80%); + --header-color: #fff; + -webkit-backdrop-filter: unset; + backdrop-filter: unset; +} + +body:not(.nav-fixed) .header.show-slide { + margin-bottom: 0; +} + +body.nav-fixed .slide-header { + margin-top: -90px; +} + +.scroll-down.nav-fixed .header.scroll-down-hide { + transform: translate3d(0, -105%, 0); + -webkit-backdrop-filter: none; + backdrop-filter: none; +} + +.slide-header .slide-text { + padding-top: 70px; +} + +.slide-header img, +.slide-header video, +.slide-header .s-desc, +.wp-block-carousel figcaption { + border-radius: 0 !important; +} + +.search-keywords { + padding: 10px; +} + +.header form { + text-align: center; +} + +.header-slider-search { + max-width: 840px; + margin: auto; + padding: 0 20px; + font-size: 17px; + top: calc(50% - 10px); + z-index: 2; +} + +.card-0 .header-slider-search { + top: calc(50% + 20px); +} + +.header-slider-search .search-input { + max-width: 800px; +} + +.header-slider-search-more { + color: #fff; +} + +.header-slider-search-more.before { + margin-bottom: 1.8em; +} + +.header-slider-search .search-type-but-average { + margin-bottom: 2em; +} + +.header-slider-search .search_keywords, +.header-slider-search .search-type-but-average .but { + --this-bg: rgba(255, 255, 255, 0.8); + --this-color: #888; +} + +.filter-blur .header-slider-search .search_keywords, +.filter-blur .header-slider-search .search-type-but-average .but { + -webkit-backdrop-filter: saturate(5) blur(20px); + backdrop-filter: saturate(5) blur(20px); +} + +.header-slider-search .search-type-but-average .but.active { + --this-bg: var(--focus-color); + --this-color: #fff; +} + +.header-slider-search .line-form { + background: rgba(255, 255, 255, 0.8); + border-radius: 100px; + padding: 0.5em 1.2em; + z-index: 1; + color: #555; +} + +.header-slider-search .line-form .dropdown > a a, +.header-slider-search .line-form .dropdown > a, +.header-slider-search .line-form .icon { + color: #555; +} + +.header-slider-search .search-input-text { + position: relative; + padding: 5px 10px; +} + +.header-slider-search .line-form-input { + padding: 0; +} + +.header-slider-search .search-keywords > p, +.header-slider-search .search-input-text .line-form-line { + display: none; +} + +.header-slider-search .search-keywords { + text-align: center; +} + +.header-slider-search .search_keywords { + padding: 3px 5px; + font-size: 0.85em; +} + +.header-slider-search .line-form .abs-right { + margin-top: 0; + margin-right: -6px; +} + +.header-slider-card { + margin-top: -40px; + z-index: 1; +} + +.header-slider-card .zib-widget { + margin-bottom: 0; +} + +.filter-blur .header-slider-search .line-form, +.filter-blur .header-slider-card .zib-widget { + -webkit-backdrop-filter: saturate(5) blur(20px); + backdrop-filter: saturate(5) blur(20px); +} + +.filter-blur .header-slider-card .zib-widget { + background: var(--blur-bg); +} + +/*------------导航栏-----------*/ +.nav-fixed .header { + position: fixed; + z-index: 999; + width: 100%; + top: 0; + -webkit-backdrop-filter: saturate(5) blur(20px); + backdrop-filter: saturate(5) blur(20px); +} + +.mobile-navbar + .fixed-body { + z-index: 996; +} + +.navbar-top { + margin: 0; + border-radius: 0 !important; + font-size: 15px; +} + +badge.top, +.navbar-top badge { + transform: translate(-5px, -10px) scale(0.85); + margin-right: -10px; +} + +.navbar-top badge { + transform: translate(-10px, -10px) scale(0.85); +} + +.navbar-top .navbar-nav { + margin-left: 20px; + margin-right: 20px; +} + +.navbar-top .navbar-right { + padding-right: 0; +} + +.navbar-right ~ .navbar-right { + padding-right: 5px; +} + +.navbar-top .navbar-but .but { + font-size: 0.8em; + padding: 0.3em 1em !important; + margin: 0.4em; +} + +.navbar-top .navbar-but .but.hover-show { + z-index: 99; +} + +.navbar-top .navbar-brand { + padding: 7px 10px; +} + +.navbar-logo > img { + height: 36px; + padding: 0 30px; +} + +.navbar-top .nav > li > a { + padding-left: 10px; + padding-right: 10px; + border-radius: 4px; + display: inline-flex !important; + overflow: hidden; + align-items: center; +} + +.navbar-top .nav > li > a > .menu-item-title { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} + +.menu-item > a > .but { + transform: translateY(-2px); + margin: -0.3em; +} + +.navbar-top .nav li a:focus, +.navbar-top .nav li a:hover, +.current-menu-item > a { + background-color: transparent; + color: var(--focus-color); +} + +.navbar-top .fa { + transition: 0.2s; +} + +.navbar-collapse .search-input { + background: var(--main-shadow); +} + +.navbar-collapse .search-input ~ .dropdown-menu { + margin-top: 15px; +} + +.navbar-collapse .dropdown-menu .search-input { + background: transparent; + padding: 10px; + width: 460px; +} + +.navbar-nav li a, +.navbar-nav li { + max-height: 50px; + position: relative; +} + +.navbar-top .sub-menu, +.theme-popover { + visibility: hidden; + position: absolute; + min-width: 120px; + background-color: var(--main-bg-color); + padding: 10px 5px; + z-index: 99; + border-radius: var(--main-radius); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); +} + +.navbar-top li:hover > .sub-menu { + visibility: unset; +} + +.navbar-top .sub-menu .sub-menu { + left: 100%; + top: -10px; +} + +.theme-popover { + display: unset; +} + +.navbar-search { + visibility: hidden; + transform: translateY(-101%); + padding: 10px 30px; + padding-top: 80px; + background: var(--main-bg-color); + height: auto; + opacity: 1; + box-shadow: 0px 14px 10px rgba(0, 0, 0, 0.08); + z-index: 998; + transition: all 0.5s; + border-radius:0 0 var(--main-radius) var(--main-radius); +} + +.navbar-search.show { + visibility: unset; + transform: translateY(0); +} + +.navbar-top .navbar-right .sub-menu { + right: 0; +} + +.navbar-top .sub-menu li > a { + white-space: nowrap; + display: block; + padding: 5px 15px; +} + +.navbar-top .sub-menu li > a:hover { + transform: translateX(5px); +} + +.navbar-top .sub-menu .menu-item-has-children > a { + padding-right: 35px; +} + +.navbar-top .sub-menu .menu-item-has-children > a > .fa-angle-down { + position: absolute; + right: 15px; + top: 8px; +} + +.navbar-top .newadd-btns { + opacity: 1 !important; +} + +.navbar-top .drop-newadd { + width: 137px; + padding: 7px 10px; +} + +.navbar-top .drop-newadd::before { + top: -4px; + left: auto; + right: 27px; +} + +/******----导航栏高级子菜单----*********/ +.menu-graphic-card-item, +.submenu-multi-column-links .links-items .link-item > * { + --main-color: var(--muted-2-color); +} + +.mobile-navbar .menu-graphic-card-item .title, +.mobile-navbar .submenu-multi-column-links .links-items .link-item > * { + font-size: 0.9em; +} + +.mobile-navbar .sub-menu.senior-submenu .container { + background: var(--muted-border-color); + border-radius: var(--main-radius); + padding: 4px 10px; + margin: 4px 0; +} + +.sub-menu.senior-submenu ~ .sub-menu { + display: none; +} + +.navbar-top .sub-menu.senior-submenu { + position: fixed; + left: 0; + right: 0; + box-shadow: 0px 10px 10px rgba(0, 0, 0, 0.05); + border-radius: 0 0 var(--main-radius) var(--main-radius); + padding: 20px 0; + top: 59px; +} + +.links-columns-box .link-item > * { + padding: 8px 5px; + display: flex; + align-items: center; +} + +.navbar-top .links-column { + margin: 10px; +} + +.navbar-top .links-columns-box .column-title { + border-bottom: 1px solid var(--main-border-color); + font-size: 1.1em; + margin-bottom: 8px; +} + +.links-columns-box .link-item .icon-hover-show { + opacity: 0; + transition: 0.3s; +} + +.navbar-top .links-columns-box .link-item > a:hover > .icon-hover-show { + opacity: 1; + transform: translate3d(10px, 0, 0); +} + +.links-columns-box .link-item img { + width: 24px; + height: 24px; + border-radius: 4px; + margin-right: 6px; + flex-shrink: 0; + object-fit: cover; +} + +.mobile-navbar .links-columns-box .link-item > * { + padding: 5px 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 110px; + display: block; +} + +.mobile-navbar .links-columns-box .link-item img { + width: 24px; + height: 24px; +} + +/*导航栏高级子菜单:图文卡片菜单*/ +.navbar-top .menu-graphic-card-item { + margin: 20px 10px; +} + +.navbar-top .submenu-graphic-card { + --img-size: 130px; +} +.navbar-top .submenu-graphic-card.size-sm { + --img-size: 80px; +} +.navbar-top .submenu-graphic-card.size-xs { + --img-size: 180px; +} +.navbar-top .submenu-graphic-card.size-lg { + --img-size: 240px; +} +.mobile-navbar .submenu-graphic-card.size-sm { + --img-size: 68px; +} +.mobile-navbar .submenu-graphic-card { + --img-size: 107px; +} +.mobile-navbar .submenu-graphic-card.size-xs { + --img-size: 140px; +} +.mobile-navbar .submenu-graphic-card.size-lg { + --img-size: 180px; +} + +.menu-graphic-card-item .img-box { + width: var(--img-size); + height: calc(var(--img-size) * var(--img-scale, 1)); + border-radius: var(--main-radius); + overflow: hidden; + position: relative; +} + +.navbar-top .links-column, +.navbar-top .menu-graphic-card-item { + opacity: 0; + transition: 0.6s; + transform: translate3d(0, 20px, 0); +} + +.navbar-top li:hover .links-column, +.navbar-top li:hover .menu-graphic-card-item { + transition-delay: var(--ani-delay); + opacity: 1; + transform: none; +} + +.menu-graphic-card-item .title.text-ellipsis { + width: var(--img-size); + text-align: center; +} + +.sub-user-box { + width: 242px; + word-break: break-word; + white-space: normal; +} + +.mobile-navbar.top .sub-user-box { + width: auto; + text-align: center; + margin: auto; + max-width: 400px; +} + +.mobile-navbar.top .sub-user-box > ul { + overflow: hidden; +} + +.sub-user-box .list-inline > li + li { + width: calc(100% - 48px); +} + +.sub-user-box .social-login-item.button-lg { + width: 110px !important; + font-size: 13px; +} + +.sub-user-box .user-name { + width: calc(100% - 3em); +} + +.sub-user-box .author-tag > * { + max-height: 41px; + overflow: hidden; + flex: auto; +} + +.sub-vip-card { + font-size: 12px; + padding: 0 !important; + background: transparent !important; + border: none !important; + box-shadow: none !important; +} + +.sub-vip-card .vip-card { + display: inline-block; + width: 240px; + margin: 5px; + box-shadow: 0 0 10px var(--main-shadow); +} + +.sub-vip-card .vip-baicon.abs-center { + font-size: 10em; + left: 55%; +} + +.sub-vip-card .payvip-button { + font-size: 1em !important; + margin: 0 !important; +} + +.sub-vip-card .vip-name { + font-size: 16px; +} + +.sub-vip-card .vip-img { + display: none; +} + +.sub-user-box .payvip-icon { + padding: 8px 0; +} + +.header-user-href > a { + display: inline-block; + padding-top: 10px; + min-width: 58px; +} + +.header-user-href > a:hover { + opacity: 0.7; +} + +.header-user-href > a div + div { + font-size: 12px; +} + +.header-user-href .toggle-radius { + width: 36px; + height: 36px; +} + + + .container { + width: 95%; + } +} + +@media (max-width: 991px) { + .nav.navbar-nav { + position: absolute; + top: 0; + background: var(--header-bg); + box-shadow: 0px 4px 10px var(--main-shadow); + left: 0; + right: 0; + padding: 0 20px; + margin: 0; + border-radius: var(--main-radius); + transition: 0.3s; + opacity: 0; + transform: translateY(40px); + visibility: hidden; + } + + .nav.navbar-nav.show { + opacity: 1; + z-index: 998; + visibility: unset; + transform: translateY(68px); + } + + .navbar-nav > li:before { + top: 0; + } + + .navbar-logo > img { + padding-left: 8px; + padding-right: 8px; + } + + .container-footer { + padding-left: 0; + padding-right: 0; + } + + .footer { + padding: 20px 0; + } + + .footer .container-footer { + text-align: center; + } + + .footer .list-inline > li + li + li { + float: unset; + } + + .footer .list-inline .hidden-xs { + display: none; + } + + .show-nav-but.hide { + display: unset !important; + } + + body .sidebar.show-sidebar { + display: unset; + width: 100%; + margin-left: 0; + } + + .site-layout-2 .content-layout { + margin-right: auto; + } + + .site-layout-3 .content-layout { + margin-left: auto; + } + + .sidebar { + display: none; + } + + #u_sign .modal-dialog { + width: auto; + margin: 30px; + } + + .search-page-ajaxpager .author-minicard { + width: calc(50% - 16px); + } +} + +@media (max-width: 767px) { + .container { + width: 100%; + } + + .container.full-sm { + padding: 0; + } + + .full-widget-sm { + diff --git a/assets/css/frontend/nav/zib-navbar-fixed.css b/assets/css/frontend/nav/zib-navbar-fixed.css new file mode 100644 index 00000000..78d75946 --- /dev/null +++ b/assets/css/frontend/nav/zib-navbar-fixed.css @@ -0,0 +1,33 @@ +/* Zibll-aligned fixed header + reserves (AKSRT) */ +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); } +} +body.nav-fixed .header:not(.show-slide) { + position: fixed; top: 0; left: 0; right: 0; width: 100%; z-index: 999; +} +body.nav-fixed .header-slider-container, +body.nav-fixed .header-components-container, +body.nav-fixed .header-components.slide-header, +body.nav-fixed .slide-header { + margin-top: calc(var(--nav-fixed-reserve, 88px) * -1) !important; + position: relative; + z-index: 1; +} +.scroll-down.nav-fixed .header.scroll-down-hide { + transform: translate3d(0, -105%, 0); + -webkit-backdrop-filter: none; + backdrop-filter: none; +} +body.nav-fixed: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.1) 80%); + --header-color: #fff; + -webkit-backdrop-filter: unset; + backdrop-filter: unset; +} diff --git a/assets/css/frontend/nav/zib-senior-submenu.css b/assets/css/frontend/nav/zib-senior-submenu.css new file mode 100644 index 00000000..1ce48647 --- /dev/null +++ b/assets/css/frontend/nav/zib-senior-submenu.css @@ -0,0 +1,221 @@ +/* Senior submenus (graphic card / multi-column) */ +.sub-menu.senior-submenu .container { + background: var(--muted-border-color); + border-radius: var(--main-radius); + padding: 4px 10px; + margin: 4px 0; +} + +.sub-menu.senior-submenu ~ .sub-menu { + display: none; +} + +.navbar-top .sub-menu.senior-submenu { + position: fixed; + left: 0; + right: 0; + box-shadow: 0px 10px 10px rgba(0, 0, 0, 0.05); + border-radius: 0 0 var(--main-radius) var(--main-radius); + padding: 20px 0; + top: 59px; +} + +.links-columns-box .link-item > * { + padding: 8px 5px; + display: flex; + align-items: center; +} + +.navbar-top .links-column { + margin: 10px; +} + +.navbar-top .links-columns-box .column-title { + border-bottom: 1px solid var(--main-border-color); + font-size: 1.1em; + margin-bottom: 8px; +} + +.links-columns-box .link-item .icon-hover-show { + opacity: 0; + transition: 0.3s; +} + +.navbar-top .links-columns-box .link-item > a:hover > .icon-hover-show { + opacity: 1; + transform: translate3d(10px, 0, 0); +} + +.links-columns-box .link-item img { + width: 24px; + height: 24px; + border-radius: 4px; + margin-right: 6px; + flex-shrink: 0; + object-fit: cover; +} + +.mobile-navbar .links-columns-box .link-item > * { + padding: 5px 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 110px; + display: block; +} + +.mobile-navbar .links-columns-box .link-item img { + width: 24px; + height: 24px; +} + +/*导航栏高级子菜单:图文卡片菜单*/ +.navbar-top .menu-graphic-card-item { + margin: 20px 10px; +} + +.navbar-top .submenu-graphic-card { + --img-size: 130px; +} +.navbar-top .submenu-graphic-card.size-sm { + --img-size: 80px; +} +.navbar-top .submenu-graphic-card.size-xs { + --img-size: 180px; +} +.navbar-top .submenu-graphic-card.size-lg { + --img-size: 240px; +} +.mobile-navbar .submenu-graphic-card.size-sm { + --img-size: 68px; +} +.mobile-navbar .submenu-graphic-card { + --img-size: 107px; +} +.mobile-navbar .submenu-graphic-card.size-xs { + --img-size: 140px; +} +.mobile-navbar .submenu-graphic-card.size-lg { + --img-size: 180px; +} + +.menu-graphic-card-item .img-box { + width: var(--img-size); + height: calc(var(--img-size) * var(--img-scale, 1)); + border-radius: var(--main-radius); + overflow: hidden; + position: relative; +} + +.navbar-top .links-column, +.navbar-top .menu-graphic-card-item { + opacity: 0; + transition: 0.6s; + transform: translate3d(0, 20px, 0); +} + +.navbar-top li:hover .links-column, +.navbar-top li:hover .menu-graphic-card-item { + transition-delay: var(--ani-delay); + opacity: 1; + transform: none; +} + +.menu-graphic-card-item .title.text-ellipsis { + width: var(--img-size); + text-align: center; +} + +.sub-user-box { + width: 242px; + word-break: break-word; + white-space: normal; +} + +.mobile-navbar.top .sub-user-box { + width: auto; + text-align: center; + margin: auto; + max-width: 400px; +} + +.mobile-navbar.top .sub-user-box > ul { + overflow: hidden; +} + +.sub-user-box .list-inline > li + li { + width: calc(100% - 48px); +} + +.sub-user-box .social-login-item.button-lg { + width: 110px !important; + font-size: 13px; +} + +.sub-user-box .user-name { + width: calc(100% - 3em); +} + +.sub-user-box .author-tag > * { + max-height: 41px; + overflow: hidden; + flex: auto; +} + +.sub-vip-card { + font-size: 12px; + padding: 0 !important; + background: transparent !important; + border: none !important; + box-shadow: none !important; +} + +.sub-vip-card .vip-card { + display: inline-block; + width: 240px; + margin: 5px; + box-shadow: 0 0 10px var(--main-shadow); +} + +.sub-vip-card .vip-baicon.abs-center { + font-size: 10em; + left: 55%; +} + +.sub-vip-card .payvip-button { + font-size: 1em !important; + margin: 0 !important; +} + +.sub-vip-card .vip-name { + font-size: 16px; +} + +.sub-vip-card .vip-img { + display: none; +} + +.sub-user-box .payvip-icon { + padding: 8px 0; +} + +.header-user-href > a { + display: inline-block; + padding-top: 10px; + min-width: 58px; +} + +.header-user-href > a:hover { + opacity: 0.7; +} + +.header-user-href > a div + div { + font-size: 12px; +} + +.header-user-href .toggle-radius { + width: 36px; + height: 36px; +} + + diff --git a/assets/js/aksrt-runtime.js b/assets/js/aksrt-runtime.js index 76e71527..de10e024 100644 --- a/assets/js/aksrt-runtime.js +++ b/assets/js/aksrt-runtime.js @@ -276,7 +276,7 @@ top = target.getBoundingClientRect().top + window.pageYOffset; } - var navFixed = document.querySelector('.site-nav.is-fixed, .site-header.is-fixed'); + var navFixed = document.querySelector('body.nav-fixed .header, .navbar.navbar-top'); var headerOffset = navFixed ? (navFixed.offsetHeight + 20) : 0; top = Math.max(0, top - headerOffset - (extraOffset || 0)); diff --git a/assets/js/core/dropdown.js b/assets/js/core/dropdown.js index ff0c0255..014fbda4 100644 --- a/assets/js/core/dropdown.js +++ b/assets/js/core/dropdown.js @@ -1,65 +1,10 @@ /** - * AKSRT Dropdown Menus — user, auth, post-meta + * AKSRT Dropdown Menus — post-meta only. + * Header user box uses CSS hover (.navbar-top .sub-menu) per zibll contract. */ (function () { "use strict"; - function initUserDropdown() { - var toggle = document.getElementById("user-menu-toggle"); - var menu = document.getElementById("user-dropdown"); - if (!toggle || !menu) return; - - function close() { - menu.classList.remove("is-open"); - toggle.setAttribute("aria-expanded", "false"); - } - - toggle.addEventListener("click", function (e) { - e.stopPropagation(); - var open = !menu.classList.contains("is-open"); - menu.classList.toggle("is-open", open); - toggle.setAttribute("aria-expanded", open ? "true" : "false"); - }); - - document.addEventListener("click", function (e) { - if (!menu.contains(e.target) && e.target !== toggle) close(); - }); - document.addEventListener("touchstart", function (e) { - if (!menu.contains(e.target) && e.target !== toggle) close(); - }, { passive: true }); - document.addEventListener("keydown", function (e) { - if (e.key === "Escape") close(); - }); - } - - function initAuthDropdown() { - var trigger = document.getElementById("nav-auth-trigger"); - var container = document.getElementById("nav-auth-dropdown"); - if (!trigger || !container) return; - - function close() { - container.classList.remove("is-open"); - trigger.setAttribute("aria-expanded", "false"); - } - - trigger.addEventListener("click", function (e) { - e.stopPropagation(); - var open = !container.classList.contains("is-open"); - container.classList.toggle("is-open", open); - trigger.setAttribute("aria-expanded", open ? "true" : "false"); - }); - - document.addEventListener("click", function (e) { - if (!container.contains(e.target) && e.target !== trigger) close(); - }); - document.addEventListener("touchstart", function (e) { - if (!container.contains(e.target) && e.target !== trigger) close(); - }, { passive: true }); - document.addEventListener("keydown", function (e) { - if (e.key === "Escape") close(); - }); - } - function initPostDropMeta() { document.querySelectorAll('.post-drop-meta').forEach(function (wrap) { if (!wrap || wrap.dataset.aksrtDropMetaBound === '1') return; @@ -80,10 +25,10 @@ document.querySelectorAll('.post-drop-meta.is-open').forEach(function (other) { if (other !== wrap) { other.classList.remove('is-open'); - var otherTrigger = other.querySelector('.post-drop-meta__toggle'); - var otherMenu = other.querySelector('.post-drop-meta__menu'); - if (otherTrigger) otherTrigger.setAttribute('aria-expanded', 'false'); - if (otherMenu) otherMenu.setAttribute('aria-hidden', 'true'); + var t = other.querySelector('.post-drop-meta__toggle'); + var m = other.querySelector('.post-drop-meta__menu'); + if (t) t.setAttribute('aria-expanded', 'false'); + if (m) m.setAttribute('aria-hidden', 'true'); } }); wrap.classList.toggle('is-open', open); @@ -94,16 +39,19 @@ document.addEventListener('click', function (e) { if (!wrap.contains(e.target)) close(); }); - document.addEventListener('touchstart', function (e) { - if (!wrap.contains(e.target)) close(); - }, { passive: true }); document.addEventListener('keydown', function (e) { if (e.key === 'Escape') close(); }); }); } - window.AksrtFrontend.register("aksrt-user-dropdown", function () { initUserDropdown(); }); - window.AksrtFrontend.register("aksrt-auth-dropdown", function () { initAuthDropdown(); }); - window.AksrtFrontend.register("aksrt-post-drop-meta", function () { initPostDropMeta(); }); + if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') { + window.AksrtFrontend.register('aksrt-post-drop-meta', function () { + initPostDropMeta(); + }); + } else if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initPostDropMeta); + } else { + initPostDropMeta(); + } })(); diff --git a/assets/js/core/nav.js b/assets/js/core/nav.js index 2d6ce7c4..86c3d09a 100644 --- a/assets/js/core/nav.js +++ b/assets/js/core/nav.js @@ -1,54 +1,88 @@ /** - * AKSRT Navigation Drawer + * AKSRT nav chrome — zibll contract (body.mobile-navbar-show, folding, chevron). + * Drawer open/close is handled by aksrt-runtime [data-toggle-class]. */ (function () { "use strict"; - function initNavDrawer() { - var toggle = document.getElementById("nav-toggle"); - var drawer = document.getElementById("nav-drawer"); - var backdrop = document.getElementById("nav-drawer-backdrop"); - var closeBtn = document.getElementById("nav-drawer-close"); - if (!toggle || !drawer || !backdrop) return; + function appendChevrons() { + document.querySelectorAll(".navbar-top li.menu-item-has-children > a").forEach(function (a) { + if (a.querySelector(".fa-angle-down")) return; + var i = document.createElement("i"); + i.className = "fa fa-angle-down ml6"; + i.setAttribute("aria-hidden", "true"); + a.appendChild(i); + }); + } - var openIcon = toggle.querySelector(".nav-toggle-open"); - var closeIcon = toggle.querySelector(".nav-toggle-close"); + function navFolding() { + var collapse = document.querySelector(".navbar-collapse"); + var nav = document.querySelector(".navbar-nav"); + if (!collapse || !nav) return; + if (window.innerWidth <= 996) return; - function setOpen(open) { - toggle.setAttribute("aria-expanded", open ? "true" : "false"); - drawer.classList.toggle("is-open", open); - backdrop.classList.toggle("is-open", open); - drawer.setAttribute("aria-hidden", open ? "false" : "true"); - backdrop.setAttribute("aria-hidden", open ? "false" : "true"); - document.body.style.overflow = open ? "hidden" : ""; - if (openIcon && closeIcon) { - openIcon.style.display = open ? "none" : ""; - closeIcon.style.display = open ? "" : "none"; + 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")) { + surplus -= child.offsetWidth || 0; } - if (open && typeof window.__aksrtCloseSearchPanel === "function") { - window.__aksrtCloseSearchPanel(); + }); + var headerEl = document.querySelector(".navbar-header"); + surplus -= (headerEl ? headerEl.offsetWidth : 0) + 40; + + var navsWidth = 66; + var foldItems = []; + Array.prototype.forEach.call(nav.children, function (li) { + navsWidth += li.offsetWidth || 0; + if (navsWidth > surplus) { + foldItems.push(li); } - } - - setOpen(false); - - toggle.addEventListener("click", function () { - setOpen(!drawer.classList.contains("is-open")); - }); - backdrop.addEventListener("click", function () { - setOpen(false); - }); - if (closeBtn) closeBtn.addEventListener("click", function () { - setOpen(false); }); + if (!foldItems.length) return; + var more = document.createElement("li"); + more.className = "menu-item menu-item-has-children aksrt-nav-more"; + more.innerHTML = + ''; + var sub = more.querySelector(".sub-menu"); + foldItems.forEach(function (li) { + sub.appendChild(li); + }); + nav.appendChild(more); + } + + function bindDrawerA11y() { + // Esc closes mobile nav via body class. document.addEventListener("keydown", function (e) { - if (e.key === "Escape" && drawer.classList.contains("is-open")) { - setOpen(false); - toggle.focus(); + if (e.key === "Escape" && document.body.classList.contains("mobile-navbar-show")) { + document.body.classList.remove("mobile-navbar-show"); } }); } - window.AksrtFrontend.register("aksrt-nav-drawer", function () { initNavDrawer(); }); + function init() { + appendChevrons(); + try { + navFolding(); + } catch (err) { + // ignore fold failures + } + bindDrawerA11y(); + } + + if (window.AksrtFrontend && typeof window.AksrtFrontend.register === "function") { + window.AksrtFrontend.register("aksrt-nav-chrome", function () { + init(); + }); + } else if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init); + } else { + init(); + } })(); diff --git a/assets/js/core/scroll.js b/assets/js/core/scroll.js index be7dfafe..18da5c11 100644 --- a/assets/js/core/scroll.js +++ b/assets/js/core/scroll.js @@ -136,28 +136,16 @@ } function initNavScrollState() { - var nav = document.querySelector(".site-nav"); - - var sliderContainer = document.querySelector(".header-components-container"); - if (nav && sliderContainer) { - nav.classList.add("nav-transparent"); - } - - var scrollDownHide = document.body.classList.contains("scroll-down-hide"); + // Zibll: hide is CSS on body.scroll-down + header.scroll-down-hide. + // Keep is-scrolled on .header for optional consumers. + var header = document.querySelector(".header"); var ticking = false; function update() { ticking = false; var body = document.body; - if (nav) { - nav.classList.toggle("is-scrolled", body.classList.contains("body-scroll")); - } - if (nav && scrollDownHide) { - if (body.classList.contains("scroll-down")) { - nav.classList.add("is-hidden"); - } else { - nav.classList.remove("is-hidden"); - } + if (header) { + header.classList.toggle("is-scrolled", body.classList.contains("body-scroll")); } } diff --git a/assets/js/enhancements.js b/assets/js/enhancements.js index 28e1d60a..6bf0bbd0 100644 --- a/assets/js/enhancements.js +++ b/assets/js/enhancements.js @@ -525,7 +525,7 @@ * 菜单项超出容器宽度时折叠进「更多」下拉 * ============================================================ */ function initNavOverflow() { - var nav = document.querySelector('.site-nav .nav-menu, .site-nav .primary-nav, .header-nav__menu'); + var nav = document.querySelector('.navbar-top .nav.navbar-nav'); if (!nav) return; // 创建「更多」按钮 diff --git a/assets/js/messages.js b/assets/js/messages.js index cde72bf8..24091fc2 100644 --- a/assets/js/messages.js +++ b/assets/js/messages.js @@ -137,7 +137,7 @@ // Numeric badge inside the user dropdown only — never target the pure avatar // .nav-user__dot.aksrt-notification-badge (no textContent on a dot indicator). - var headerBadge = document.querySelector('.user-dropdown__badge'); + var headerBadge = document.querySelector('.aksrt-notification-badge'); if (headerBadge) { headerBadge.textContent = String(nextUnread); headerBadge.style.display = nextUnread > 0 ? '' : 'none'; diff --git a/assets/js/mini-touch.js b/assets/js/mini-touch.js index 5d0da975..c1ebe328 100644 --- a/assets/js/mini-touch.js +++ b/assets/js/mini-touch.js @@ -182,10 +182,10 @@ el.style.transform = 'translateX(100%)'; el.style.opacity = '0'; setTimeout(function () { - el.classList.remove('is-open'); el.style.transform = ''; el.style.opacity = ''; - document.body.classList.remove('nav-open'); + // Zibll contract: drawer open state lives on body. + document.body.classList.remove('mobile-navbar-show'); }, 200); } else { el.style.transition = 'transform 0.2s, opacity 0.2s'; diff --git a/assets/js/share-data-builder.js b/assets/js/share-data-builder.js index 532d47e3..a0f42c4c 100644 --- a/assets/js/share-data-builder.js +++ b/assets/js/share-data-builder.js @@ -30,7 +30,7 @@ function siteName() { return ( metaContent(['meta[property="og:site_name"]']) || - (document.querySelector('.site-title, .logo-text, .site-nav__logo') || {}).textContent || + (document.querySelector('.site-title, .logo-text, .navbar-logo') || {}).textContent || document.title || '' ).trim(); diff --git a/functions.php b/functions.php index a8463623..fa1f21c9 100644 --- a/functions.php +++ b/functions.php @@ -136,6 +136,9 @@ if (get_option('aksrt_flushed_version') !== AKSRT_VERSION) { \AKSRT\Core\Setup::get_instance()->init(); \AKSRT\Core\Scripts::get_instance()->init(); \AKSRT\Admin\Admin::get_instance()->init(); +if (class_exists('\AKSRT\Admin\NavMenuOptions')) { + \AKSRT\Admin\NavMenuOptions::init(); +} \AKSRT\Auth\OAuth::get_instance()->init(); if (class_exists('\AKSRT\Auth\WeixinGzh')) { \AKSRT\Auth\WeixinGzh::get_instance()->init(); diff --git a/header.php b/header.php index 415f247e..52d7f818 100644 --- a/header.php +++ b/header.php @@ -1,6 +1,6 @@ true, 'nav_style' => 'default', 'max_width' => 1200, - 'scroll_down_hide' => false, - 'layout' => '2', + 'scroll_down_hide' => true, + 'layout' => '1', 'mobile_layout' => 'center', 'mobile_align' => 'left', ]; @@ -29,10 +29,12 @@ if (class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\S } $aksrt_has_initial_theme = in_array($aksrt_initial_theme_mode, ['dark', 'light'], true); -$aksrt_body_classes = ['header-nav-style-' . $aksrt_header_settings['nav_style']]; -// Zibll contract: body.nav-fixed drives top padding + hero pull-under-nav. +$aksrt_body_classes = []; +// Zibll: body.nav-fixed drives top padding + hero pull-under-nav. if (!empty($aksrt_header_settings['fixed'])) { $aksrt_body_classes[] = 'nav-fixed'; +} else { + $aksrt_body_classes[] = 'has-static-nav'; } if ($aksrt_initial_theme_mode === 'dark') { @@ -41,19 +43,16 @@ if ($aksrt_initial_theme_mode === 'dark') { } elseif ($aksrt_initial_theme_mode === 'light') { $aksrt_body_classes[] = 'white-theme'; } -if (!$aksrt_header_settings['fixed']) { - $aksrt_body_classes[] = 'has-static-nav'; -} -if (!empty($aksrt_header_settings['scroll_down_hide'])) { +if (!empty($aksrt_header_settings['scroll_down_hide']) && !empty($aksrt_header_settings['fixed'])) { $aksrt_body_classes[] = 'scroll-down-hide'; } $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']; -// 全局背景系统 +// Global background system. $aksrt_bg_settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_global_background_settings() : []; -$aksrt_bg_styles = []; +$aksrt_bg_styles = []; if (!empty($aksrt_bg_settings['bg_color'])) { $aksrt_bg_styles[] = '--aksrt-bg-color:' . sanitize_hex_color($aksrt_bg_settings['bg_color']); } @@ -95,456 +94,14 @@ if (!empty($aksrt_bg_styles)) { > - + 'post', - 'types' => ['post' => __('文章', 'aksrt')], - 'category_id' => 0, - 'category_enabled' => false, - 'categories' => [], - ]; - $aksrt_search_placeholder = function_exists('aksrt_get_search_placeholder') ? aksrt_get_search_placeholder() : __('开启精彩搜索', 'aksrt'); - $aksrt_search_type = $aksrt_search_state['type']; - $aksrt_search_type_options = $aksrt_search_state['types']; - $aksrt_search_category_enabled = !empty($aksrt_search_state['category_enabled']); - $aksrt_search_category_id = (int) ($aksrt_search_state['category_id'] ?? 0); - $aksrt_search_categories = is_array($aksrt_search_state['categories'] ?? null) ? $aksrt_search_state['categories'] : []; - $aksrt_search_hot_keywords = function_exists('aksrt_get_search_hot_keywords') ? aksrt_get_search_hot_keywords() : []; - $aksrt_brand_logo_src = ''; - $aksrt_mobile_menu_location = has_nav_menu('mobile') ? 'mobile' : 'primary'; - $aksrt_show_user_entry = !empty($aksrt_header_settings['user_menu']); - $aksrt_current_user = wp_get_current_user(); - $aksrt_user_credit = function_exists('aksrt_user_credits') && is_user_logged_in() ? (int) aksrt_user_credits(get_current_user_id()) : 0; - $aksrt_currency_name = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_currency_name() : __('积分', 'aksrt'); - $aksrt_orders_url = function_exists('aksrt_get_orders_page_url') ? aksrt_get_orders_page_url() : ''; - $aksrt_publish_url = function_exists('aksrt_get_new_post_url') ? aksrt_get_new_post_url() : ''; - if ($aksrt_publish_url === '') { - $aksrt_publish_url = admin_url('post-new.php'); - } - $aksrt_messages_url = function_exists('aksrt_get_messages_page_url') ? aksrt_get_messages_page_url() : ''; - $aksrt_unread_count = (is_user_logged_in() && function_exists('aksrt_get_user_unread_notification_count')) - ? (int) aksrt_get_user_unread_notification_count(get_current_user_id()) - : 0; - $aksrt_user_desc = is_user_logged_in() - ? (string) get_user_meta(get_current_user_id(), 'description', true) - : ''; - if ($aksrt_user_desc === '' && is_user_logged_in()) { - $aksrt_user_desc = (string) $aksrt_current_user->user_email; - } - $aksrt_vip_badge_html = (is_user_logged_in() && function_exists('aksrt_render_vip_badge')) - ? aksrt_render_vip_badge(get_current_user_id(), 'sm') - : ''; - $aksrt_theme_settings_url = function_exists('aksrt_get_admin_settings_url') - ? aksrt_get_admin_settings_url('') - : admin_url('admin.php?page=aksrt-settings'); - $aksrt_theme_toggle_positions = class_exists('\AKSRT\Settings\Settings') - ? \AKSRT\Settings\Settings::get_theme_mode_button_positions() - : ['pc_nav', 'm_menu']; - $aksrt_can_publish = !empty($aksrt_header_settings['publish_btn']) && ( - current_user_can('manage_options') - || (class_exists('\AKSRT\Core\FrontSubmit') && \AKSRT\Core\FrontSubmit::get_instance()->can_submit()) - ); - $aksrt_show_header_slide = function_exists('aksrt_header_slide_is_show') - ? aksrt_header_slide_is_show() - : false; - // Dual class contract: keep site-nav/* while aligning navbar/navbar-top with zibll. - $aksrt_nav_classes = [ - 'site-nav', - 'navbar', - 'navbar-top', - 'site-nav--' . $aksrt_header_settings['nav_style'], - $aksrt_header_settings['fixed'] ? 'site-nav--fixed' : 'site-nav--static', - ]; - $aksrt_nav_styles = []; - if ((int) $aksrt_header_settings['max_width'] > 0) { - $aksrt_nav_styles[] = '--nav-max-width:' . (int) $aksrt_header_settings['max_width'] . 'px'; - } - $aksrt_logo_parts = []; - if ($aksrt_logo_url !== '' || $aksrt_logo_dark_url !== '') { - $aksrt_logo_more = $aksrt_logo_width > 0 - ? 'style="height:36px;width:auto;max-width:' . max(36, (int) $aksrt_logo_width) . 'px"' - : 'style="height:36px;width:auto"'; - $aksrt_logo_html = function_exists('aksrt_get_adaptive_theme_img') - ? aksrt_get_adaptive_theme_img($aksrt_logo_url, $aksrt_logo_dark_url, $aksrt_brand_name, $aksrt_logo_more) - : ''; - if ($aksrt_logo_html !== '') { - $aksrt_logo_parts[] = $aksrt_logo_html; - } - $aksrt_brand_logo_src = $aksrt_logo_url !== '' ? $aksrt_logo_url : $aksrt_logo_dark_url; - } - $aksrt_has_brand_logo = !empty($aksrt_logo_parts); - if ($aksrt_has_brand_logo) { - $aksrt_brand_logo_html = implode('', $aksrt_logo_parts); - } - $aksrt_brand_mark = strtoupper(substr(preg_replace('/\s+/', '', wp_strip_all_tags($aksrt_brand_name)), 0, 1)); - if ($aksrt_brand_mark === '') { - $aksrt_brand_mark = 'A'; - } - $aksrt_brand_class = 'site-brand'; - if ($aksrt_has_brand_logo) { - $aksrt_brand_class .= ' site-brand--has-logo'; - } - $aksrt_brand_drawer_class = $aksrt_brand_class . ' site-brand--drawer'; - $aksrt_header_classes = [ - 'header', - 'header-layout-' . $aksrt_header_settings['layout'], - ]; - // Fixed state lives on body.nav-fixed (zibll); nav keeps site-nav--fixed. - if ($aksrt_show_header_slide) { - $aksrt_header_classes[] = 'show-slide'; - } - if (!empty($aksrt_header_settings['scroll_down_hide']) && !empty($aksrt_header_settings['fixed'])) { - $aksrt_header_classes[] = 'scroll-down-hide'; - } -?> -
- -
- - - - - - - - - - (+ mobile drawer), before page fluid/main. +// Zibll: header slider after (+ mobile drawer), before page fluid/main. if (function_exists('aksrt_header_slide')) { aksrt_header_slide(); } diff --git a/inc/bootstrap.php b/inc/bootstrap.php index 2828ee90..e1f09f57 100644 --- a/inc/bootstrap.php +++ b/inc/bootstrap.php @@ -47,6 +47,9 @@ require_once AKSRT_DIR . '/inc/helpers/aksrt-ajax-list.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-links.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-header-userbox.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-header-menu-buttons.php'; +require_once AKSRT_DIR . '/inc/helpers/header/aksrt-header-render.php'; +require_once AKSRT_DIR . '/inc/helpers/header/aksrt-header-navbar.php'; +require_once AKSRT_DIR . '/inc/helpers/header/aksrt-header-mobile.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-runtime-win.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-identity.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-graphic-card.php'; diff --git a/inc/helpers/aksrt-header-menu-buttons.php b/inc/helpers/aksrt-header-menu-buttons.php index 144c915a..3ad6e0e0 100644 --- a/inc/helpers/aksrt-header-menu-buttons.php +++ b/inc/helpers/aksrt-header-menu-buttons.php @@ -14,19 +14,14 @@ if (!function_exists('aksrt_get_header_newposts_config')) : function aksrt_get_header_newposts_config(): array { $cfg = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_header_newposts_config() - : ['enabled' => true, 'text' => __('发布', 'aksrt'), 'radius' => 20, 'class' => '']; - $all = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : []; - // Prefer publish_btn from header nav when newposts not explicitly set. + : ['enabled' => true, 'text' => __('发布', 'aksrt'), 'radius' => 20, 'class' => '', 'types' => []]; $nav = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_header_nav_settings() : ['publish_btn' => true]; - if (empty($cfg['enabled']) && !empty($nav['publish_btn'])) { - $cfg['enabled'] = true; - } - $types = []; - if (!empty($all['header_newposts_types']) && is_array($all['header_newposts_types'])) { - $types = array_values(array_filter(array_map('sanitize_key', $all['header_newposts_types']))); - } + // Single source: newposts enabled AND header publish switch. + $cfg['enabled'] = !empty($cfg['enabled']) && !empty($nav['publish_btn']); + + $types = is_array($cfg['types'] ?? null) ? $cfg['types'] : []; if (!$types) { $types = ['post']; if (class_exists('\AKSRT\Settings\Settings')) { @@ -229,7 +224,7 @@ if (!function_exists('aksrt_render_header_menu_buttons')) : $show_vip = false; if (class_exists('\AKSRT\Settings\Settings')) { $vs = \AKSRT\Settings\Settings::get_payment_vip_settings(); - $show_vip = !empty($vs['enabled']); + $show_vip = !empty($vs['enabled']) && !empty(\AKSRT\Settings\Settings::get_header_nav_settings()['nav_pay_vip']); } if ($show_vip) { $is_vip = false; diff --git a/inc/helpers/aksrt-header-userbox.php b/inc/helpers/aksrt-header-userbox.php index b892e5c1..34c8bf62 100644 --- a/inc/helpers/aksrt-header-userbox.php +++ b/inc/helpers/aksrt-header-userbox.php @@ -117,6 +117,10 @@ if (!function_exists('aksrt_header_payvip_bar')) : return ''; } + $nav = class_exists('\AKSRT\Settings\Settings') + ? \AKSRT\Settings\Settings::get_header_nav_settings() + : ['nav_user_pay_vip' => true]; + $level = function_exists('aksrt_get_user_vip_level') ? (int) aksrt_get_user_vip_level($user_id) : 0; if ($level > 0) { $levels = is_array($vip['levels'] ?? null) ? $vip['levels'] : []; @@ -139,6 +143,11 @@ if (!function_exists('aksrt_header_payvip_bar')) : return $html; } + // Open-VIP CTA only when user-box switch is on (zibll nav_user_pay_vip). + if (empty($nav['nav_user_pay_vip'])) { + return ''; + } + return '
' . '' . '' diff --git a/inc/helpers/header/aksrt-header-mobile.php b/inc/helpers/header/aksrt-header-mobile.php new file mode 100644 index 00000000..9981e3d7 --- /dev/null +++ b/inc/helpers/header/aksrt-header-mobile.php @@ -0,0 +1,124 @@ + $ctx + */ + function aksrt_nav_mobile_shell(array $ctx): void { + $align = (string) ($ctx['mobile_align'] ?? 'left'); + if (!in_array($align, ['top', 'left', 'right'], true)) { + $align = 'left'; + } + $m_layout = (string) ($ctx['mobile_layout'] ?? 'center'); + $fixed = !empty($ctx['fixed']); + + $touch_attr = ''; + if ($align !== 'top') { + // mini-touch.js reads data-mini-touch; direction via data-touch-direction. + $touch_attr = ' data-mini-touch="mobile-nav" data-touch-direction="' . esc_attr($align) . '"'; + } + + echo '
'; + echo ''; + + if (!$fixed) { + aksrt_navbar_header($ctx); + } + + $brand_name = (string) ($ctx['brand_name'] ?? get_bloginfo('name')); + $logo_inner = !empty($ctx['logo_html']) + ? $ctx['logo_html'] + : '' . esc_html($brand_name) . ''; + + echo '
'; + echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo ''; + echo '
'; + + aksrt_nav_mobile_content($ctx, $m_layout); + + if (is_active_sidebar('mobile_nav_fluid')) { + echo '
'; + dynamic_sidebar('mobile_nav_fluid'); + echo '
'; + } + + echo ''; + echo '
'; + echo '
'; + } +endif; + +if (!function_exists('aksrt_nav_mobile_content')) : + /** + * Mobile menu list + top toggles + user box. + * + * @param array $ctx + * @param string $m_layout center|left + */ + function aksrt_nav_mobile_content(array $ctx, string $m_layout = 'center'): void { + $top_btns = ''; + if (in_array('m_menu', (array) ($ctx['theme_positions'] ?? []), true)) { + $top_btns .= ''; + $top_btns .= ''; + $top_btns .= ''; + $top_btns .= ''; + } + if ($m_layout !== 'center' && !empty($ctx['search_enabled'])) { + $top_btns .= ''; + } + if ($top_btns !== '') { + echo '
' . $top_btns . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + $location = has_nav_menu('mobile') ? 'mobile' : 'primary'; + $walker = class_exists('\AKSRT\Frontend\NavWalker') + ? new \AKSRT\Frontend\NavWalker( + function_exists('aksrt_get_menu_decoration_settings') + ? aksrt_get_menu_decoration_settings() + : [] + ) + : null; + + $args = [ + 'container' => false, + 'echo' => false, + 'fallback_cb' => false, + 'depth' => 2, + 'items_wrap' => '', + 'theme_location' => $location, + ]; + if ($walker) { + $args['walker'] = $walker; + } + + $menu = has_nav_menu($location) ? (string) wp_nav_menu($args) : ''; + if ($menu === '' && $location !== 'primary' && has_nav_menu('primary')) { + $args['theme_location'] = 'primary'; + $menu = (string) wp_nav_menu($args); + } + if ($menu === '' && is_super_admin()) { + $menu = ''; + } + echo $menu; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + + // User box at bottom of drawer. + if (!empty($ctx['user_menu'])) { + echo aksrt_header_user_box_html($ctx); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + } +endif; diff --git a/inc/helpers/header/aksrt-header-navbar.php b/inc/helpers/header/aksrt-header-navbar.php new file mode 100644 index 00000000..8ebbf371 --- /dev/null +++ b/inc/helpers/header/aksrt-header-navbar.php @@ -0,0 +1,344 @@ + false, + 'echo' => false, + 'fallback_cb' => false, + 'items_wrap' => '', + 'theme_location' => $location, + ]; + if ($walker) { + $args['walker'] = $walker; + } + if (!wp_is_mobile()) { + $args['depth'] = 0; + } else { + $args['depth'] = 3; + } + + $menu = ''; + if (has_nav_menu($location)) { + $menu = (string) wp_nav_menu($args); + } + + if ($menu === '' && is_super_admin()) { + $menu = ''; + } + + if ($echo) { + echo $menu; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + return; + } + return $menu; + } +endif; + +if (!function_exists('aksrt_navbar_header')) : + /** + * Logo + mobile toggles (zibll zib_navbar_header). + * + * @param array $ctx + */ + function aksrt_navbar_header(array $ctx): void { + $brand_name = (string) ($ctx['brand_name'] ?? get_bloginfo('name')); + $logo_inner = !empty($ctx['logo_html']) + ? $ctx['logo_html'] + : '' . esc_html($brand_name) . ''; + + $logo = ''; + + $button = ''; + + if (($ctx['mobile_layout'] ?? 'center') === 'center' && !empty($ctx['search_enabled'])) { + $button .= ''; + } + + echo ''; + } +endif; + +if (!function_exists('aksrt_get_menu_search')) : + /** + * Layout-2 inline search form (zibll zib_get_menu_search simplified). + * + * @param array $ctx + */ + function aksrt_get_menu_search(array $ctx): void { + if (is_search()) { + return; + } + $placeholder = (string) ($ctx['search_placeholder'] ?? __('搜索内容', 'aksrt')); + $state = is_array($ctx['search_state'] ?? null) ? $ctx['search_state'] : []; + $type = (string) ($state['type'] ?? 'post'); + $cat_enabled = !empty($state['category_enabled']); + $cat_id = (int) ($state['category_id'] ?? 0); + + echo ''; + } +endif; + +if (!function_exists('aksrt_header_user_box_html')) : + /** + * User dropdown inner content for nav sub-menu (logged-in / guest). + * + * @param array $ctx + */ + function aksrt_header_user_box_html(array $ctx): string { + static $html = null; + if ($html !== null) { + return $html; + } + + if (empty($ctx['user_menu'])) { + $html = ''; + return $html; + } + + $user_id = (int) ($ctx['user_id'] ?? 0); + $con = ''; + + if ($user_id > 0) { + $user = $ctx['current_user'] instanceof WP_User ? $ctx['current_user'] : wp_get_current_user(); + $display_name = $user->display_name ?: $user->user_login; + $avatar = get_avatar($user_id, 44, '', esc_attr($display_name), ['loading' => 'lazy']); + $desc = (string) ($ctx['user_desc'] ?? ''); + $vip_badge = function_exists('aksrt_render_vip_badge') ? aksrt_render_vip_badge($user_id, 'sm') : ''; + $payvip = function_exists('aksrt_header_payvip_bar') ? aksrt_header_payvip_bar($user_id) : ''; + // nav_user_pay_vip gate: empty payvip when switch off for non-VIP CTA only — helper already handles VIP status. + if (empty($ctx['nav_user_pay_vip']) && $payvip !== '' && strpos($payvip, 'is-vip') === false) { + $payvip = ''; + } + $checkin = function_exists('aksrt_header_user_checkin_btn') ? aksrt_header_user_checkin_btn($user_id) : ''; + $wallet = function_exists('aksrt_header_user_wallet_mini') ? aksrt_header_user_wallet_mini($user_id) : ''; + $badges = function_exists('aksrt_header_user_badges_row') ? aksrt_header_user_badges_row($user_id) : ''; + $unread = (int) ($ctx['unread_count'] ?? 0); + $msg_url = (string) ($ctx['messages_url'] ?? ''); + + $msg_icon = ''; + if ($msg_url !== '') { + $msg_icon = ''; + $msg_icon .= ''; + if ($unread > 0) { + $msg_icon .= '' . esc_html((string) min(99, $unread)) . ''; + } + $msg_icon .= ''; + } + + $href = '
'; + $href .= ''; + $href .= '
'; + $href .= '
' . esc_html__('用户中心', 'aksrt') . '
'; + + $orders = (string) ($ctx['orders_url'] ?? ''); + if ($orders !== '') { + $href .= ''; + $href .= '
'; + $href .= '
' . esc_html__('我的订单', 'aksrt') . '
'; + } + + if (!empty($ctx['can_publish'])) { + $pub_text = class_exists('\AKSRT\Settings\Settings') + ? (string) (\AKSRT\Settings\Settings::get_all()['header_user_newposts_text'] ?? '') + : ''; + if ($pub_text === '') { + $pub_text = __('发布内容', 'aksrt'); + } + $href .= ''; + $href .= '
'; + $href .= '
' . esc_html($pub_text) . '
'; + } + + $href .= ''; + $href .= '
'; + $href .= '
' . esc_html__('退出登录', 'aksrt') . '
'; + $href .= '
'; + + if (is_super_admin()) { + $href .= ''; + } + + $con = ''; + $con .= $payvip ? $payvip : ''; + $con .= $checkin ? '
' . $checkin . '
' : ''; + $con .= $wallet ? '
' . $wallet . '
' : ''; + $con .= $badges; + $con .= '
'; + $con .= '
' . $href . '
'; + } else { + $vip_guest = ''; + if (!empty($ctx['nav_user_pay_vip']) && class_exists('\AKSRT\Settings\Settings')) { + $vs = \AKSRT\Settings\Settings::get_payment_vip_settings(); + if (!empty($vs['enabled'])) { + $vip_guest = ''; + } + } + + $href = $vip_guest; + $href .= '
'; + $href .= ''; + if (get_option('users_can_register')) { + $href .= ''; + } + $href .= '
' + . esc_html__('找回密码', 'aksrt') . '
'; + $href .= '
'; + + $con = '
' . $href . '
'; + if (class_exists('\AKSRT\Auth\OAuth') && method_exists(\AKSRT\Auth\OAuth::get_instance(), 'render_social_buttons')) { + $oauth_html = (string) \AKSRT\Auth\OAuth::get_instance()->render_social_buttons(__('快速登录', 'aksrt')); + if ($oauth_html !== '') { + $con .= ''; + $con .= ''; + } + } + } + + $html = '
' . $con . '
'; + return $html; + } +endif; + +if (!function_exists('aksrt_menu_button')) : + /** + * Right cluster: fold, publish/VIP, theme, user, search (zibll zib_menu_button). + * + * @param array $ctx + */ + function aksrt_menu_button(array $ctx): void { + $layout = (string) ($ctx['layout'] ?? '1'); + $user_id = (int) ($ctx['user_id'] ?? 0); + $button = ''; + + // Publish + VIP (respect nav_pay_vip). + if (function_exists('aksrt_render_header_menu_buttons')) { + $button .= aksrt_render_header_menu_buttons(); + } + + // Theme toggle. + $radius_but = ''; + if (in_array('pc_nav', (array) ($ctx['theme_positions'] ?? []), true)) { + $radius_but .= ''; + $radius_but .= ''; + $radius_but .= ''; + $radius_but .= ''; + } + if ($radius_but !== '') { + $button .= ''; + } + + // User + search. + $sign_but = ''; + $user_sub = ''; + if (!empty($ctx['user_menu'])) { + $user_sub = '
' . aksrt_header_user_box_html($ctx) . '
'; + if ('2' === $layout) { + if ($user_id > 0) { + $avatar = get_avatar($user_id, 36, '', '', ['loading' => 'lazy', 'class' => 'avatar']); + $sign_but = '
  • ' . $avatar . '
  • '; + } else { + $sign_but = '
  • '; + if (get_option('users_can_register')) { + $sign_but .= '
  • '; + } + } + } else { + $trigger = $user_id > 0 + ? get_avatar($user_id, 36, '', '', ['loading' => 'lazy']) + : ''; + $cls = $user_id > 0 ? 'navbar-avatar' : 'btn signin-loader'; + $dot = ($user_id > 0 && (int) ($ctx['unread_count'] ?? 0) > 0) + ? '' + : ''; + $sign_but = '
  • ' + . $trigger . $dot . '
  • '; + } + } + + $search_but = ''; + if ('2' !== $layout && !empty($ctx['search_enabled'])) { + $search_but = '
  • '; + } + + $right_but = ''; + + $fold = ''; + + $html = $fold; + if ('3' === $layout) { + $html .= $right_but . $button; + } else { + $html .= $button . $right_but; + } + + echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } +endif; diff --git a/inc/helpers/header/aksrt-header-render.php b/inc/helpers/header/aksrt-header-render.php new file mode 100644 index 00000000..f68d8b0e --- /dev/null +++ b/inc/helpers/header/aksrt-header-render.php @@ -0,0 +1,190 @@ + + */ + function aksrt_get_header_nav_context(): array { + static $ctx = null; + if ($ctx !== null) { + return $ctx; + } + + $settings = class_exists('\AKSRT\Settings\Settings') + ? \AKSRT\Settings\Settings::get_header_nav_settings() + : [ + 'fixed' => true, + 'search_enabled' => true, + 'user_menu' => true, + 'publish_btn' => true, + 'mobile_drawer' => true, + 'nav_style' => 'default', + 'max_width' => 1200, + 'scroll_down_hide' => true, + 'layout' => '1', + 'mobile_layout' => 'center', + 'mobile_align' => 'left', + 'nav_pay_vip' => true, + 'nav_user_pay_vip' => true, + ]; + + $site_name = get_bloginfo('name'); + $brand_name = function_exists('aksrt_get_site_logo_text') ? aksrt_get_site_logo_text() : $site_name; + $brand_name = $brand_name !== '' ? $brand_name : $site_name; + $logo_url = function_exists('aksrt_get_site_logo_url') ? trim((string) aksrt_get_site_logo_url()) : ''; + $logo_dark = function_exists('aksrt_get_site_logo_dark_url') ? trim((string) aksrt_get_site_logo_dark_url()) : ''; + $logo_width = function_exists('aksrt_get_site_logo_width') ? aksrt_get_site_logo_width() : 150; + $logo_more = $logo_width > 0 + ? 'style="height:36px;width:auto;max-width:' . max(36, (int) $logo_width) . 'px"' + : 'style="height:36px;width:auto"'; + $logo_html = ''; + if ($logo_url !== '' || $logo_dark !== '') { + $logo_html = function_exists('aksrt_get_adaptive_theme_img') + ? aksrt_get_adaptive_theme_img($logo_url, $logo_dark, $brand_name, $logo_more) + : ''; + } + + $theme_positions = class_exists('\AKSRT\Settings\Settings') + ? \AKSRT\Settings\Settings::get_theme_mode_button_positions() + : ['pc_nav', 'm_menu']; + + $can_publish = !empty($settings['publish_btn']) && ( + current_user_can('manage_options') + || (class_exists('\AKSRT\Core\FrontSubmit') && \AKSRT\Core\FrontSubmit::get_instance()->can_submit()) + ); + + $show_slide = function_exists('aksrt_header_slide_is_show') + ? aksrt_header_slide_is_show() + : false; + + $current_user = wp_get_current_user(); + $user_id = (int) $current_user->ID; + $user_desc = $user_id > 0 ? (string) get_user_meta($user_id, 'description', true) : ''; + if ($user_desc === '' && $user_id > 0) { + $user_desc = (string) $current_user->user_email; + } + + $ctx = [ + 'settings' => $settings, + 'layout' => (string) ($settings['layout'] ?? '1'), + 'mobile_layout' => (string) ($settings['mobile_layout'] ?? 'center'), + 'mobile_align' => (string) ($settings['mobile_align'] ?? 'left'), + 'fixed' => !empty($settings['fixed']), + 'scroll_down_hide' => !empty($settings['scroll_down_hide']) && !empty($settings['fixed']), + 'search_enabled' => !empty($settings['search_enabled']), + 'user_menu' => !empty($settings['user_menu']), + 'mobile_drawer' => !empty($settings['mobile_drawer']), + 'nav_pay_vip' => !isset($settings['nav_pay_vip']) || !empty($settings['nav_pay_vip']), + 'nav_user_pay_vip' => !isset($settings['nav_user_pay_vip']) || !empty($settings['nav_user_pay_vip']), + 'max_width' => (int) ($settings['max_width'] ?? 1200), + 'brand_name' => $brand_name, + 'logo_html' => $logo_html, + 'has_logo' => $logo_html !== '', + 'theme_positions' => is_array($theme_positions) ? $theme_positions : [], + 'can_publish' => $can_publish, + 'show_slide' => $show_slide, + 'current_user' => $current_user, + 'user_id' => $user_id, + 'user_desc' => $user_desc, + 'unread_count' => ($user_id > 0 && function_exists('aksrt_get_user_unread_notification_count')) + ? (int) aksrt_get_user_unread_notification_count($user_id) + : 0, + 'messages_url' => function_exists('aksrt_get_messages_page_url') ? aksrt_get_messages_page_url() : '', + 'orders_url' => function_exists('aksrt_get_orders_page_url') ? aksrt_get_orders_page_url() : '', + 'publish_url' => function_exists('aksrt_get_new_post_url') ? aksrt_get_new_post_url() : admin_url('post-new.php'), + 'theme_settings_url' => function_exists('aksrt_get_admin_settings_url') + ? aksrt_get_admin_settings_url('') + : admin_url('admin.php?page=aksrt-settings'), + 'search_state' => function_exists('aksrt_get_search_form_state') + ? aksrt_get_search_form_state() + : [ + 'type' => 'post', + 'types' => ['post' => __('文章', 'aksrt')], + 'category_id' => 0, + 'category_enabled' => false, + 'categories' => [], + ], + 'search_placeholder' => function_exists('aksrt_get_search_placeholder') + ? aksrt_get_search_placeholder() + : __('开启精彩搜索', 'aksrt'), + 'search_hot_keywords' => function_exists('aksrt_get_search_hot_keywords') + ? aksrt_get_search_hot_keywords() + : [], + ]; + + if ($ctx['publish_url'] === '') { + $ctx['publish_url'] = admin_url('post-new.php'); + } + + return $ctx; + } +endif; + +if (!function_exists('aksrt_header')) : + /** + * Render top header + mobile drawer (+ optional slide after caller). + */ + function aksrt_header(): void { + $ctx = aksrt_get_header_nav_context(); + $layout = $ctx['layout']; + $header_class = [ + 'header', + 'header-layout-' . $layout, + ]; + if (!empty($ctx['show_slide'])) { + $header_class[] = 'show-slide'; + } + if (!empty($ctx['scroll_down_hide'])) { + $header_class[] = 'scroll-down-hide'; + } + + $nav_class = [ + 'navbar', + 'navbar-top', + $ctx['mobile_layout'], + ]; + + $nav_style = ''; + if ((int) $ctx['max_width'] > 0) { + $nav_style = ' style="--nav-max-width:' . (int) $ctx['max_width'] . 'px"'; + } + + echo '
    '; + echo '
    '; + + if (!empty($ctx['mobile_drawer'])) { + aksrt_nav_mobile_shell($ctx); + } + + if (function_exists('aksrt_render_signout_modal')) { + aksrt_render_signout_modal(); + } + } +endif; diff --git a/inc/helpers/menu-decoration.php b/inc/helpers/menu-decoration.php index 6a6d2e5e..a75c8a9b 100644 --- a/inc/helpers/menu-decoration.php +++ b/inc/helpers/menu-decoration.php @@ -1,9 +1,6 @@ */ function aksrt_get_menu_decoration_settings(): array { - static $cache = null; - if ($cache !== null) { - return $cache; - } + static $cache = null; + if ($cache !== null) { + return $cache; + } - if (class_exists('\AKSRT\Settings\Settings')) { - $all = \AKSRT\Settings\Settings::get_all(); - } else { - $all = []; - } + $all = class_exists('\AKSRT\Settings\Settings') + ? \AKSRT\Settings\Settings::get_all() + : []; - $cache = [ - 'icon_enabled' => !empty($all['menu_icon_enabled']), - 'badge_enabled' => !empty($all['menu_badge_enabled']), - 'submenu_style' => in_array($all['menu_submenu_style'] ?? '', ['dropdown', 'mega', 'cards'], true) - ? $all['menu_submenu_style'] - : 'dropdown', - 'submenu_columns' => max(1, min(5, (int) ($all['menu_submenu_columns'] ?? 1))), - 'submenu_width' => max(160, min(800, (int) ($all['menu_submenu_width'] ?? 220))), - 'hover_effect' => in_array($all['menu_hover_effect'] ?? '', ['underline', 'background', 'color', 'none'], true) - ? $all['menu_hover_effect'] - : 'underline', - 'item_padding' => max(4, min(32, (int) ($all['menu_item_padding'] ?? 12))), - 'mobile_style' => in_array($all['menu_mobile_style'] ?? '', ['drawer', 'fullscreen', 'dropdown'], true) - ? $all['menu_mobile_style'] - : 'drawer', - ]; + $cache = [ + 'icon_enabled' => !isset($all['menu_icon_enabled']) || !empty($all['menu_icon_enabled']), + 'badge_enabled' => !isset($all['menu_badge_enabled']) || !empty($all['menu_badge_enabled']), + ]; - return $cache; + return $cache; } /** - * Get decoration meta for a specific menu item. + * Read one menu item option from post meta aksrt_menu_options. * - * Reads CSS class and title attribute from the menu item to extract: - * - icon: FA5 class (e.g. "fas fa-home") - * - badge_text: badge label (e.g. "新", "热") - * - badge_type: badge color type (e.g. "new", "hot", "info") - * - submenu_style: override for this item's submenu + * @param int $id Menu item ID. + * @param string $key Option key. + * @param mixed $default Default. + * @return mixed + */ +function aksrt_menu_pz(int $id, string $key = '', $default = '') { + static $options = []; + if (!isset($options[$id])) { + $raw = get_post_meta($id, 'aksrt_menu_options', true); + $options[$id] = is_array($raw) ? $raw : []; + } + if ($key === '') { + return $options[$id]; + } + return $options[$id][$key] ?? $default; +} + +/** + * Decoration for a menu item (meta only). * - * @param WP_Post $menu_item The menu item post object. - * @return array + * @param WP_Post|object $menu_item Menu item. + * @return array{icon:string,badge_text:string,badge_class:string,submenu_s:bool,submenu_type:string} */ function aksrt_get_menu_item_decoration($menu_item): array { - $classes = (array) ($menu_item->classes ?? []); - $title = (string) ($menu_item->title ?? ''); - $attr = (string) ($menu_item->attr_title ?? ''); - - $result = [ - 'icon' => '', - 'badge_text' => '', - 'badge_type' => 'new', - 'submenu_style' => '', - ]; - - // Extract icon from CSS classes (pattern: icon-* or fa-*) - foreach ($classes as $cls) { - $cls = trim($cls); - if (strpos($cls, 'fa-') === 0 || strpos($cls, 'fas ') === 0 || strpos($cls, 'fab ') === 0 || strpos($cls, 'far ') === 0) { - $result['icon'] = $cls; - break; - } - if (strpos($cls, 'icon-') === 0) { - $result['icon'] = 'fas fa-' . substr($cls, 5); - break; - } - } - - // Extract badge from title attribute (pattern: [新] or [热:hot]) - if (preg_match('/^\[([^\]:]+)(?::(\w+))?\]/', $title, $m)) { - $result['badge_text'] = $m[1]; - $result['badge_type'] = $m[2] ?? 'new'; - // Remove badge markup from display title - $menu_item->title = trim(substr($title, strlen($m[0]))); - } - - // Extract submenu style override from classes - foreach ($classes as $cls) { - if (strpos($cls, 'submenu-') === 0) { - $style = substr($cls, 8); - if (in_array($style, ['dropdown', 'mega', 'cards'], true)) { - $result['submenu_style'] = $style; - } - break; - } - } - - return $result; + $id = (int) ($menu_item->ID ?? 0); + return [ + 'icon' => (string) aksrt_menu_pz($id, 'icon', ''), + 'badge_text' => (string) aksrt_menu_pz($id, 'badge', ''), + 'badge_class' => (string) aksrt_menu_pz($id, 'badge_class', ''), + 'submenu_s' => !empty(aksrt_menu_pz($id, 'submenu_s', false)), + 'submenu_type' => sanitize_key((string) aksrt_menu_pz($id, 'submenu_type', '')), + ]; } /** - * Render a menu item icon if enabled. - * - * @param string $icon_class FA5 icon class. - * @param bool $enabled Whether icons are enabled globally. - * @return string HTML string. + * @param string $icon_class FA5 / icon class. + * @param bool $enabled Global switch. */ function aksrt_render_menu_icon(string $icon_class, bool $enabled = true): string { - if (!$enabled || $icon_class === '') { - return ''; - } - return ''; + if (!$enabled || $icon_class === '') { + return ''; + } + // Allow bare "fa-home" → "fas fa-home". + if (strpos($icon_class, 'fa') === false) { + $icon_class = 'fas fa-' . ltrim($icon_class, '-'); + } elseif (strpos($icon_class, 'fa-') === 0 && strpos($icon_class, ' ') === false) { + $icon_class = 'fas ' . $icon_class; + } + return ''; } /** - * Render a menu item badge if enabled. - * * @param string $text Badge text. - * @param string $type Badge type (new, hot, info, etc.). - * @param bool $enabled Whether badges are enabled globally. - * @return string HTML string. + * @param string $class Extra class (jb-red etc.). + * @param bool $enabled Global switch. */ -function aksrt_render_menu_badge(string $text, string $type = 'new', bool $enabled = true): string { - if (!$enabled || $text === '') { - return ''; - } - return '' . esc_html($text) . ''; -} - -/** - * Get CSS classes for a menu item based on decoration settings. - * - * @param array $decoration Decoration meta from aksrt_get_menu_item_decoration(). - * @param array $settings Global decoration settings. - * @return string Space-separated CSS classes. - */ -function aksrt_get_menu_item_classes(array $decoration, array $settings): string { - $classes = ['aksrt-nav__item']; - - if ($settings['hover_effect'] !== 'none') { - $classes[] = 'aksrt-nav__item--hover-' . $settings['hover_effect']; - } - - if ($decoration['icon'] !== '') { - $classes[] = 'aksrt-nav__item--has-icon'; - } - - if ($decoration['badge_text'] !== '') { - $classes[] = 'aksrt-nav__item--has-badge'; - } - - return implode(' ', $classes); +function aksrt_render_menu_badge(string $text, string $class = '', bool $enabled = true): string { + if (!$enabled || $text === '') { + return ''; + } + $cls = trim('menu-item-badge ' . $class); + return ' ' . esc_html($text) . ''; } diff --git a/src/AKSRT/Admin/NavMenuOptions.php b/src/AKSRT/Admin/NavMenuOptions.php new file mode 100644 index 00000000..51900b3e --- /dev/null +++ b/src/AKSRT/Admin/NavMenuOptions.php @@ -0,0 +1,112 @@ + +

    + +

    +

    + +

    +

    + +

    +

    + +

    +

    + +

    +

    + +

    + sanitize_text_field((string) ($raw['icon'] ?? '')), + 'badge' => sanitize_text_field((string) ($raw['badge'] ?? '')), + 'badge_class' => sanitize_html_class((string) ($raw['badge_class'] ?? '')), + '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']; + } + if (!empty($existing['multi_column_links_opts'])) { + $clean['multi_column_links_opts'] = $existing['multi_column_links_opts']; + } + update_post_meta($menu_item_db_id, 'aksrt_menu_options', $clean); + } +} diff --git a/src/AKSRT/Admin/Schema/SchemaPageDisplay.php b/src/AKSRT/Admin/Schema/SchemaPageDisplay.php index e63c069c..492ece41 100644 --- a/src/AKSRT/Admin/Schema/SchemaPageDisplay.php +++ b/src/AKSRT/Admin/Schema/SchemaPageDisplay.php @@ -14,10 +14,10 @@ class SchemaPageDisplay { 'domain' => 'page_display', 'title' => '页面&显示', 'sections' => [ - 'header-nav' => [ + 'header-nav' => [ 'key' => 'header-nav', 'title' => '顶部导航栏', - 'version' => '1.0.0', + 'version' => '1.1.0', 'option_key' => 'aksrt_settings', 'groups' => [ ['key' => 'layout', 'title' => '导航栏布局', 'fields' => [ @@ -28,25 +28,7 @@ class SchemaPageDisplay { ['label' => '毛玻璃', 'value' => 'blur'], ]], ['key' => 'header_max_width', 'label' => '导航栏最大宽度', 'type' => 'number', 'default' => 1200, 'min' => 0, 'max' => 1600], - ['key' => 'header_scroll_down_hide', 'label' => '下滑隐藏导航栏', 'type' => 'switch', 'default' => false], - ]], - ['key' => 'components', 'title' => '导航组件', 'fields' => [ - ['key' => 'header_search_enabled', 'label' => '搜索入口', 'type' => 'switch', 'default' => true], - ['key' => 'header_user_menu', 'label' => '用户菜单', 'type' => 'switch', 'default' => true], - ['key' => 'header_publish_btn', 'label' => '发布按钮', 'type' => 'switch', 'default' => true], - ['key' => 'header_mobile_drawer', 'label' => '移动端抽屉导航', 'type' => 'switch', 'default' => true], - ]], - ['key' => 'newposts', 'title' => '新文章提示', 'fields' => [ - ['key' => 'header_newposts_enabled', 'label' => '显示新文章提示', 'type' => 'switch', 'default' => true], - ['key' => 'header_newposts_text', 'label' => '提示文案', 'type' => 'input', 'default' => '有新文章'], - ['key' => 'header_newposts_radius', 'label' => '圆角', 'type' => 'number', 'default' => 12, 'min' => 0, 'max' => 30], - ['key' => 'header_newposts_class', 'label' => '样式类', 'type' => 'input', 'default' => ''], - ]], - ['key' => 'color', 'title' => '颜色设置', 'fields' => [ - ['key' => 'header_theme_custom', 'label' => '自定义导航栏颜色', 'type' => 'switch', 'default' => false], - ['key' => 'header_theme_bg_custom', 'label' => '自定义背景色', 'type' => 'color', 'default' => ''], - ['key' => 'header_theme_bg', 'label' => '背景颜色(备选)', 'type' => 'color', 'default' => ''], - ['key' => 'header_theme_color_custom', 'label' => '自定义文字颜色', 'type' => 'color', 'default' => ''], + ['key' => 'header_scroll_down_hide', 'label' => '下滑隐藏导航栏', 'type' => 'switch', 'default' => true], ]], ['key' => 'layout-type', 'title' => '布局类型', 'fields' => [ ['key' => 'header_layout', 'label' => '桌面端布局', 'type' => 'radio', 'default' => '1', 'options' => [ @@ -64,9 +46,35 @@ class SchemaPageDisplay { ['label' => '右侧滑出', 'value' => 'right'], ]], ]], + ['key' => 'components', 'title' => '导航组件', 'fields' => [ + ['key' => 'header_search_enabled', 'label' => '搜索入口', 'type' => 'switch', 'default' => true], + ['key' => 'header_user_menu', 'label' => '用户菜单', 'type' => 'switch', 'default' => true], + ['key' => 'header_publish_btn', 'label' => '发布按钮', 'type' => 'switch', 'default' => true], + ['key' => 'header_mobile_drawer', 'label' => '移动端抽屉导航', 'type' => 'switch', 'default' => true], + ['key' => 'header_nav_pay_vip', 'label' => '导航栏开通会员按钮', 'type' => 'switch', 'default' => true], + ['key' => 'header_nav_user_pay_vip', 'label' => '用户框开通会员按钮', 'type' => 'switch', 'default' => true], + ]], + ['key' => 'newposts', 'title' => '发布按钮', 'fields' => [ + ['key' => 'header_newposts_enabled', 'label' => '启用发布按钮', 'type' => 'switch', 'default' => true], + ['key' => 'header_newposts_text', 'label' => '按钮文案', 'type' => 'input', 'default' => ' 发布'], + ['key' => 'header_newposts_radius', 'label' => '圆角', 'type' => 'number', 'default' => 20, 'min' => 0, 'max' => 30], + ['key' => 'header_newposts_class', 'label' => '样式类', 'type' => 'input', 'default' => ''], + ['key' => 'header_newposts_types', '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' => [ + ['key' => 'header_theme_custom', 'label' => '自定义导航栏颜色', 'type' => 'switch', 'default' => false], + ['key' => 'header_theme_bg_custom', 'label' => '自定义背景色', 'type' => 'color', 'default' => ''], + ['key' => 'header_theme_bg', 'label' => '背景颜色(备选)', 'type' => 'color', 'default' => ''], + ['key' => 'header_theme_color_custom', 'label' => '自定义文字颜色', 'type' => 'color', 'default' => ''], + ]], ], ], - 'header-components' => [ +'header-components' => [ 'key' => 'header-components', 'title' => '顶部多功能组件', 'description' => '配置顶部组件的显示范围、设备规则、轮播参数、搜索覆盖层与图标卡片。幻灯片图片/文案内容请到「Hero 轮播」「Banner 轮播」管理。', diff --git a/src/AKSRT/Core/ScriptsPart1c.php b/src/AKSRT/Core/ScriptsPart1c.php index 2e4b3be8..ed6296e9 100644 --- a/src/AKSRT/Core/ScriptsPart1c.php +++ b/src/AKSRT/Core/ScriptsPart1c.php @@ -591,10 +591,10 @@ trait ScriptsPart1c { ], 'shell' => [ 'wrapperId' => '', - 'navId' => 'site-nav', + 'navId' => 'navbar-top', 'searchPanelId' => 'nav-search-panel', - 'drawerId' => 'nav-drawer', - 'userMenuId' => 'user-dropdown', + 'drawerId' => 'mobile-navbar', + 'userMenuId' => 'sub-user-box', 'floatActionsId' => 'float-right-btns', 'tabbarId' => 'footer-tabbar', ], diff --git a/src/AKSRT/Frontend/NavWalker.php b/src/AKSRT/Frontend/NavWalker.php index 65ade310..e5ceb991 100644 --- a/src/AKSRT/Frontend/NavWalker.php +++ b/src/AKSRT/Frontend/NavWalker.php @@ -1,5 +1,7 @@ */ + private $settings; - public function __construct(array $settings = []) { - $this->settings = $settings; - } + /** @var int[] */ + public static $submenu_ids = []; - /** - * 当前处理的菜单项是否为 Mega Menu 容器 - * @var bool - */ - private $is_mega_menu = false; + /** + * @param array $settings + */ + public function __construct(array $settings = []) { + $this->settings = $settings ?: ( + function_exists('aksrt_get_menu_decoration_settings') + ? aksrt_get_menu_decoration_settings() + : ['icon_enabled' => true, 'badge_enabled' => true] + ); + } - /** - * Mega Menu 列数 - * @var int - */ - private $mega_columns = 3; + /** + * Skip WP children when parent uses senior submenu. + */ + public function display_element($element, &$children_elements, $max_depth, $depth, $args, &$output) { + if (!empty($element->menu_item_parent) && in_array((int) $element->menu_item_parent, self::$submenu_ids, true)) { + return; + } + parent::display_element($element, $children_elements, $max_depth, $depth, $args, $output); + } - /** - * Start level: open submenu with appropriate class based on style. - */ - public function start_lvl(&$output, $depth = 0, $args = null): void { - $indent = str_repeat("\t", $depth); + public function start_el(&$output, $item, $depth = 0, $args = null, $id = 0): void { + $title = '' . $item->title . ''; - if ($this->is_mega_menu && $depth === 0) { - // Mega Menu 子菜单容器 - $output .= "\n{$indent}
      mega_columns}\">\n"; - } else { - $output .= "\n{$indent}
        \n"; - } - } + $icon_enabled = !empty($this->settings['icon_enabled']); + $badge_enabled = !empty($this->settings['badge_enabled']); - /** - * Start element: add icon, badge, and classes. - */ - public function start_el(&$output, $item, $depth = 0, $args = null, $id = 0): void { - $indent = ($depth) ? str_repeat("\t", $depth) : ''; + $icon = function_exists('aksrt_menu_pz') ? (string) aksrt_menu_pz((int) $item->ID, 'icon', '') : ''; + $badge = function_exists('aksrt_menu_pz') ? (string) aksrt_menu_pz((int) $item->ID, 'badge', '') : ''; + $badge_class = function_exists('aksrt_menu_pz') ? (string) aksrt_menu_pz((int) $item->ID, 'badge_class', '') : ''; - $classes = (array) $item->classes; - $decoration = function_exists('aksrt_get_menu_item_decoration') - ? aksrt_get_menu_item_decoration($item) - : ['icon' => '', 'badge_text' => '', 'badge_type' => 'new', 'submenu_style' => '']; + if ($icon && function_exists('aksrt_render_menu_icon')) { + $title = aksrt_render_menu_icon($icon, $icon_enabled) . $title; + } + if ($badge && function_exists('aksrt_render_menu_badge')) { + $title .= aksrt_render_menu_badge($badge, $badge_class, $badge_enabled); + } - // 检测 Mega Menu 标记 - $this->is_mega_menu = false; - $this->mega_columns = 3; - if ($depth === 0) { - foreach ($classes as $cls) { - if ($cls === 'mega-menu') { - $this->is_mega_menu = true; - } - if (preg_match('/^mega-menu-columns-(\d)$/', $cls, $m)) { - $this->mega_columns = max(2, min(5, (int) $m[1])); - } - } - } + $item->title = $title; - // Build li classes - $li_classes = ['menu-item', 'menu-item-' . $item->ID]; - if (in_array('current-menu-item', $classes, true)) { - $li_classes[] = 'current-menu-item'; - } - if (in_array('menu-item-has-children', $classes, true)) { - $li_classes[] = 'menu-item-has-children'; - } - if ($this->is_mega_menu) { - $li_classes[] = 'mega-menu-item'; - } + $submenu = ''; + if (function_exists('aksrt_menu_pz') && aksrt_menu_pz((int) $item->ID, 'submenu_s') && (int) $depth === 0) { + $submenu = $this->submenu((int) $item->ID); + } + if ($submenu) { + $item->classes = is_array($item->classes) ? $item->classes : []; + $item->classes[] = 'menu-item-has-children'; + self::$submenu_ids[] = (int) $item->ID; + } - // Add decoration classes - $li_classes[] = 'aksrt-nav__item'; - if ($decoration['icon'] !== '' && !empty($this->settings['icon_enabled'])) { - $li_classes[] = 'aksrt-nav__item--has-icon'; - } - if ($decoration['badge_text'] !== '' && !empty($this->settings['badge_enabled'])) { - $li_classes[] = 'aksrt-nav__item--has-badge'; - } - $hover = $this->settings['hover_effect'] ?? 'underline'; - if ($hover !== 'none') { - $li_classes[] = 'aksrt-nav__item--hover-' . $hover; - } + parent::start_el($output, $item, $depth, $args, $id); + $output .= $submenu; + } - $class_str = implode(' ', array_unique($li_classes)); - $output .= $indent . '
      • '; + /** + * @param int $item_id + */ + public function submenu(int $item_id): string { + $type = function_exists('aksrt_menu_pz') ? sanitize_key((string) aksrt_menu_pz($item_id, 'submenu_type', '')) : ''; + $method = 'submenu_' . $type; + if ($type !== '' && method_exists($this, $method)) { + return (string) $this->$method($item_id); + } + return ''; + } - // Build link content - $icon_html = ''; - if ($decoration['icon'] !== '' && !empty($this->settings['icon_enabled'])) { - $icon_html = aksrt_render_menu_icon($decoration['icon'], true); - } + /** + * Graphic card mega menu. + */ + public function submenu_graphic_card(int $item_id): string { + $opts = function_exists('aksrt_menu_pz') ? aksrt_menu_pz($item_id, 'graphic_card_opts', []) : []; + $opts = is_array($opts) ? $opts : []; + $items = is_array($opts['items'] ?? null) ? $opts['items'] : []; + if (!$items) { + return ''; + } + $arrangement = (string) ($opts['arrangement'] ?? ''); + $lists = ''; + $i = 0; + foreach ($items as $item) { + $item_html = $this->submenu_graphic_card_item(is_array($item) ? $item : []); + if ($item_html === '') { + continue; + } + $ani = 'style="--ani-delay:' . ($i * 0.05) . 's;"'; + $cls = $arrangement === 'swiper' ? 'swiper-slide' : ''; + $lists .= '
        ' . $item_html . '
        '; + $i++; + } + if ($lists === '') { + return ''; + } + $align = (string) ($opts['align'] ?? ''); + $lists_box = '
        ' . $lists . '
        '; + if ($arrangement === 'swiper') { + $lists_box = '
        ' + . $lists . '
        '; + } + $size = !empty($opts['size']) ? ' size-' . sanitize_key((string) $opts['size']) : ''; + $img_scale = isset($opts['img_scale']) ? (int) $opts['img_scale'] : 0; + $scale_attr = ''; + if ($img_scale && $img_scale !== 100) { + $scale_attr = ' style="--img-scale:' . esc_attr((string) round($img_scale / 100, 2)) . ';"'; + } + return ''; + } - $badge_html = ''; - if ($decoration['badge_text'] !== '' && !empty($this->settings['badge_enabled'])) { - $badge_html = aksrt_render_menu_badge($decoration['badge_text'], $decoration['badge_type'], true); - } + /** + * @param array $args + */ + public function submenu_graphic_card_item(array $args): string { + if (empty($args['img'])) { + return ''; + } + $link_url = !empty($args['link']['url']) ? esc_url((string) $args['link']['url']) : 'javascript:;'; + $link_title = (string) ($args['link']['text'] ?? ''); + $link_target = !empty($args['link']['target']) ? ' target="' . esc_attr((string) $args['link']['target']) . '"' : ''; + $title = (string) ($args['title'] ?? ''); + if ($title === '' && $link_title !== '') { + $title = $link_title; + } + $badge = !empty($args['badge']) + ? '
        ' + . esc_html((string) $args['badge']) . '
        ' + : ''; + $img_html = '
        ' . $badge . '
        '; + $title_html = $title !== '' ? '
        ' . esc_html($title) . '
        ' : ''; + return ''; + } - // Mega Menu 子项:检查是否有描述文字(用作卡片副标题) - $description_html = ''; - if ($this->is_mega_menu && $depth === 1 && !empty($item->description)) { - $description_html = '' . esc_html($item->description) . ''; - } + /** + * Multi-column links mega menu. + */ + public function submenu_multi_column_links(int $item_id): string { + $opts = function_exists('aksrt_menu_pz') ? aksrt_menu_pz($item_id, 'multi_column_links_opts', []) : []; + $opts = is_array($opts) ? $opts : []; + $columns = is_array($opts['columns'] ?? null) ? $opts['columns'] : []; + if (!$columns) { + return ''; + } + $lists = ''; + $i = 1; + foreach ($columns as $column) { + if (!is_array($column)) { + continue; + } + $ani = 'style="--ani-delay:' . ($i * 0.08) . 's;"'; + $lists .= ''; + $i++; + } + return ''; + } - $atts = [ - 'href' => $item->url, - 'title' => $item->attr_title ?: '', - ]; - - $link_inner = $icon_html . '' . esc_html($item->title) . '' . $description_html . $badge_html; - - $output .= ' $value) { - if ('' !== $value) { - $output .= ' ' . $attr . '="' . esc_attr($value) . '"'; - } - } - $output .= '>' . $link_inner . ''; - } - - /** - * End element. - */ - public function end_el(&$output, $item, $depth = 0, $args = null): void { - $output .= "
      • \n"; - } + /** + * @param array $args + */ + public function submenu_multi_column_links_item(array $args, string $class = ''): string { + $title = (string) ($args['title'] ?? ''); + $link_url = (string) ($args['link']['url'] ?? ''); + $link_title = (string) ($args['link']['text'] ?? ''); + if ($title === '' && $link_title !== '') { + $title = $link_title; + } + if ($title === '') { + return ''; + } + $icon = ''; + if (!empty($args['icon']) && function_exists('aksrt_render_menu_icon')) { + $icon = aksrt_render_menu_icon((string) $args['icon'], true); + } + if (!empty($args['img_icon'])) { + $icon = ''; + } + $title_html = $icon . $title; + $class = trim(($class ? $class . ' ' : '') . 'link-item'); + if ($link_url !== '') { + $target = !empty($args['link']['target']) ? ' target="' . esc_attr((string) $args['link']['target']) . '"' : ''; + return ''; + } + return ''; + } } diff --git a/src/AKSRT/Settings/FacadeBulk.php b/src/AKSRT/Settings/FacadeBulk.php index 86e575f0..1f128931 100644 --- a/src/AKSRT/Settings/FacadeBulk.php +++ b/src/AKSRT/Settings/FacadeBulk.php @@ -14,13 +14,17 @@ trait FacadeBulk { /** * Header navigation display settings. * - * @return array{fixed:bool,search_enabled:bool,user_menu:bool,publish_btn:bool,mobile_drawer:bool,nav_style:string,max_width:int,scroll_down_hide:bool,layout:string,mobile_layout:string,mobile_align:string} + * @return array{ + * fixed:bool,search_enabled:bool,user_menu:bool,publish_btn:bool,mobile_drawer:bool, + * nav_style:string,max_width:int,scroll_down_hide:bool,layout:string,mobile_layout:string, + * mobile_align:string,nav_pay_vip:bool,nav_user_pay_vip:bool + * } */ public static function get_header_nav_settings(): array { $settings = self::get_all(); $nav_style = sanitize_key((string) ($settings['header_nav_style'] ?? 'default')); $max_width = (int) ($settings['header_max_width'] ?? 1200); - $layout = sanitize_key((string) ($settings['header_layout'] ?? '2')); + $layout = sanitize_key((string) ($settings['header_layout'] ?? '1')); $ml = sanitize_key((string) ($settings['mobile_header_layout'] ?? 'center')); $ma = sanitize_key((string) ($settings['mobile_navbar_align'] ?? 'left')); @@ -33,17 +37,20 @@ trait FacadeBulk { } return [ - 'fixed' => !isset($settings['header_fixed']) || !empty($settings['header_fixed']), - 'search_enabled' => !isset($settings['header_search_enabled']) || !empty($settings['header_search_enabled']), - 'user_menu' => !isset($settings['header_user_menu']) || !empty($settings['header_user_menu']), - 'publish_btn' => !isset($settings['header_publish_btn']) || !empty($settings['header_publish_btn']), - 'mobile_drawer' => !isset($settings['header_mobile_drawer']) || !empty($settings['header_mobile_drawer']), - 'nav_style' => $nav_style, - 'max_width' => $max_width, - 'scroll_down_hide'=> !empty($settings['header_scroll_down_hide']), - 'layout' => in_array($layout, ['1', '2', '3'], true) ? $layout : '2', - 'mobile_layout' => in_array($ml, ['center', 'left'], true) ? $ml : 'center', - 'mobile_align' => in_array($ma, ['top', 'left', 'right'], true) ? $ma : 'left', + 'fixed' => !isset($settings['header_fixed']) || !empty($settings['header_fixed']), + 'search_enabled' => !isset($settings['header_search_enabled']) || !empty($settings['header_search_enabled']), + 'user_menu' => !isset($settings['header_user_menu']) || !empty($settings['header_user_menu']), + 'publish_btn' => !isset($settings['header_publish_btn']) || !empty($settings['header_publish_btn']), + 'mobile_drawer' => !isset($settings['header_mobile_drawer']) || !empty($settings['header_mobile_drawer']), + 'nav_style' => $nav_style, + 'max_width' => $max_width, + // Zibll default: true when fixed. + 'scroll_down_hide' => !isset($settings['header_scroll_down_hide']) || !empty($settings['header_scroll_down_hide']), + 'layout' => in_array($layout, ['1', '2', '3'], true) ? $layout : '1', + 'mobile_layout' => in_array($ml, ['center', 'left'], true) ? $ml : 'center', + 'mobile_align' => in_array($ma, ['top', 'left', 'right'], true) ? $ma : 'left', + 'nav_pay_vip' => !isset($settings['header_nav_pay_vip']) || !empty($settings['header_nav_pay_vip']), + 'nav_user_pay_vip' => !isset($settings['header_nav_user_pay_vip']) || !empty($settings['header_nav_user_pay_vip']), ]; } @@ -52,17 +59,23 @@ trait FacadeBulk { /** * Header publish button configuration. * - * @return array{enabled:bool,text:string,radius:int,class:string} + * @return array{enabled:bool,text:string,radius:int,class:string,types:string[]} */ public static function get_header_newposts_config(): array { $s = self::get_all(); + $types = []; + if (!empty($s['header_newposts_types']) && is_array($s['header_newposts_types'])) { + $types = array_values(array_filter(array_map('sanitize_key', $s['header_newposts_types']))); + } + return [ // Default on like zibll nav_newposts when key unset. 'enabled' => !isset($s['header_newposts_enabled']) || !empty($s['header_newposts_enabled']), 'text' => trim((string) ($s['header_newposts_text'] ?? ' 发布')), 'radius' => max(0, min(30, (int) ($s['header_newposts_radius'] ?? 20))), 'class' => sanitize_html_class((string) ($s['header_newposts_class'] ?? '')), + 'types' => $types, ]; }