feat(nav): deep-align menu_search panel, pill radius, senior media
- Layout-2 remote-box AJAX panel (history/hot/posts) via aksrt_menu_search - Live suggestions for post/topic/product; local history for guests - Publish radius switch (zibll bool pill) mapped to CSS radius class - Senior menu editor: WP media library image picker - Active menu underline + panel CSS; acceptance/contract updates
This commit is contained in:
1 parent
2a11a889c3
commit
159a12c481
14 files changed
+494
-117
No files matched your search
@@ -51,7 +51,7 @@ export const pageDisplaySchemas = {
|
||||
mobile_navbar_align: 'left',
|
||||
header_newposts_enabled: true,
|
||||
header_newposts_text: '<i class="fa fa-pencil"></i> 发布',
|
||||
header_newposts_radius: 20,
|
||||
header_newposts_radius: true,
|
||||
header_newposts_class: '',
|
||||
header_theme_custom: false,
|
||||
header_theme_bg_custom: '',
|
||||
@@ -126,7 +126,7 @@ export const pageDisplaySchemas = {
|
||||
fields: [
|
||||
{ key: 'header_newposts_enabled', label: '启用发布按钮', type: 'switch', default: true, help: '导航栏显示前台发布入口' },
|
||||
{ key: 'header_newposts_text', label: '按钮文案', type: 'input', default: '<i class="fa fa-pencil"></i> 发布', 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_radius', label: '按钮圆角(胶囊)', type: 'switch', default: true, dependsOn: { key: 'header_newposts_enabled', value: true }, help: '开启后两端大圆角,对齐 zibll nav_newposts_radius' },
|
||||
{ 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' },
|
||||
|
||||
@@ -162,3 +162,61 @@ body.mobile-navbar-show { overflow: hidden; }
|
||||
.aksrt-sme-field { display: block; margin: 4px 0; }
|
||||
.aksrt-sme-field span { display: block; font-size: 12px; color: #646970; }
|
||||
.aksrt-sme-card, .aksrt-sme-col { border: 1px solid #dcdcde; padding: 8px; margin: 8px 0; border-radius: 4px; }
|
||||
|
||||
/* Layout-2 remote menu search panel (zibll menu_search) */
|
||||
.aksrt-remote-box[data-aksrt-menu-search-remote] {
|
||||
min-height: 8px;
|
||||
}
|
||||
.navbar-inline-search-panel {
|
||||
min-width: 300px;
|
||||
max-width: 380px;
|
||||
max-height: 70vh;
|
||||
overflow: auto;
|
||||
}
|
||||
.aksrt-menu-search-posts__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
padding: 6px 2px;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid rgba(0,0,0,.04);
|
||||
}
|
||||
.aksrt-menu-search-posts__item:last-child { border-bottom: 0; }
|
||||
.aksrt-menu-search-posts__item:hover { color: var(--focus-color); }
|
||||
.aksrt-menu-search-history__list .but,
|
||||
.navbar-inline-search-hots .but { margin: 0 4px 4px 0; }
|
||||
.loading-spot {
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin: 8px;
|
||||
border: 2px solid rgba(0,0,0,.15);
|
||||
border-top-color: var(--focus-color, #f04494);
|
||||
border-radius: 50%;
|
||||
animation: aksrt-spin .7s linear infinite;
|
||||
}
|
||||
@keyframes aksrt-spin { to { transform: rotate(360deg); } }
|
||||
.navbar-but .radius,
|
||||
.navbar-but .but.radius,
|
||||
.navbar-cta.radius {
|
||||
border-radius: 999px !important;
|
||||
}
|
||||
|
||||
/* Active menu underline (zibll-like) */
|
||||
.navbar-top .nav > li.current-menu-item > a,
|
||||
.navbar-top .nav > li.current-menu-ancestor > a {
|
||||
color: var(--focus-color);
|
||||
}
|
||||
.navbar-top .nav > li.current-menu-item > a::before,
|
||||
.navbar-top .nav > li.current-menu-ancestor > a::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
top: 0;
|
||||
height: 3px;
|
||||
border-radius: 0 0 3px 3px;
|
||||
background: var(--focus-color);
|
||||
}
|
||||
.navbar-top .nav > li > a { position: relative; }
|
||||
@@ -39,11 +39,15 @@
|
||||
};
|
||||
}
|
||||
|
||||
function field(name, label, value, type) {
|
||||
function field(name, label, value, type, withMedia) {
|
||||
type = type || 'text';
|
||||
var mediaBtn = withMedia
|
||||
? ' <button type="button" class="button aksrt-sme-pick-img" data-for="' + name + '">选择图片</button>'
|
||||
: '';
|
||||
return (
|
||||
'<label class="aksrt-sme-field"><span>' +
|
||||
label +
|
||||
mediaBtn +
|
||||
'</span><input type="' +
|
||||
type +
|
||||
'" data-name="' +
|
||||
@@ -62,7 +66,7 @@
|
||||
html += '<div class="aksrt-sme-card" data-index="' + i + '">';
|
||||
html += '<div class="aksrt-sme-card-head"><strong>卡片 #' + (i + 1) + '</strong>';
|
||||
html += ' <button type="button" class="button-link-delete aksrt-sme-del-card">删除</button></div>';
|
||||
html += field('img', '图片 URL', item.img || '');
|
||||
html += field('img', '图片 URL', item.img || '', 'text', true);
|
||||
html += field('title', '标题', item.title || '');
|
||||
html += field('badge', '徽章', item.badge || '');
|
||||
html += field('badge_class', '徽章 class', item.badge_class || '');
|
||||
@@ -236,6 +240,24 @@
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$(document).on('click', '.aksrt-sme-pick-img', function (e) {
|
||||
e.preventDefault();
|
||||
var $btn = $(this);
|
||||
var name = $btn.data('for');
|
||||
var $input = $btn.closest('.aksrt-sme-field').find('input[data-name="' + name + '"]');
|
||||
if (typeof wp === 'undefined' || !wp.media) {
|
||||
window.alert('媒体库不可用');
|
||||
return;
|
||||
}
|
||||
var frame = wp.media({ title: '选择图片', button: { text: '使用此图' }, multiple: false, library: { type: 'image' } });
|
||||
frame.on('select', function () {
|
||||
var att = frame.state().get('selection').first().toJSON();
|
||||
if (att && att.url) $input.val(att.url).trigger('change');
|
||||
});
|
||||
frame.open();
|
||||
});
|
||||
|
||||
$(boot);
|
||||
$(document).on('menu-item-added', function () {
|
||||
setTimeout(boot, 50);
|
||||
|
||||
@@ -1,112 +1,166 @@
|
||||
/**
|
||||
* Enhance layout-2 inline search with AJAX suggestions (aksrt_search_suggestions).
|
||||
* Extends existing panel search without dual systems.
|
||||
* Layout-2 navbar search — zibll remote-box + live suggestions.
|
||||
* Loads aksrt_menu_search panel (history/hot/posts), then suggests on input.
|
||||
*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function initInlineSearchSuggest() {
|
||||
var form = document.querySelector('form[data-aksrt-search-form="inline"]');
|
||||
if (!form || !window.aksrtData || !aksrtData.ajaxUrl) return;
|
||||
var HISTORY_KEY = "aksrt-mobile-search-history";
|
||||
|
||||
function ajaxUrl() {
|
||||
return (window.aksrtData && aksrtData.ajaxUrl) || "";
|
||||
}
|
||||
function nonce() {
|
||||
return (window.aksrtData && aksrtData.nonce) || "";
|
||||
}
|
||||
|
||||
function readLocalHistory() {
|
||||
try {
|
||||
var raw = localStorage.getItem(HISTORY_KEY);
|
||||
var list = raw ? JSON.parse(raw) : [];
|
||||
if (!Array.isArray(list)) return [];
|
||||
return list
|
||||
.map(function (it) {
|
||||
if (typeof it === "string") return it;
|
||||
return it && it.keyword ? String(it.keyword) : "";
|
||||
})
|
||||
.filter(Boolean)
|
||||
.slice(0, 10);
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function writeLocalHistory(keywords) {
|
||||
try {
|
||||
var items = (keywords || []).slice(0, 10).map(function (k) {
|
||||
return { keyword: k, type: "post" };
|
||||
});
|
||||
localStorage.setItem(HISTORY_KEY, JSON.stringify(items));
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function loadRemote(box) {
|
||||
if (!box || box.getAttribute("data-loaded") === "1") return;
|
||||
var url = box.getAttribute("remote-box") || box.getAttribute("data-url");
|
||||
if (!url) return;
|
||||
box.setAttribute("data-loaded", "1");
|
||||
var sep = url.indexOf("?") >= 0 ? "&" : "?";
|
||||
if (nonce()) {
|
||||
url = url + sep + "nonce=" + encodeURIComponent(nonce());
|
||||
}
|
||||
fetch(url, { credentials: "same-origin" })
|
||||
.then(function (r) {
|
||||
return r.text();
|
||||
})
|
||||
.then(function (html) {
|
||||
if (!html || html === "0") {
|
||||
box.innerHTML = "";
|
||||
return;
|
||||
}
|
||||
box.outerHTML = html;
|
||||
var form = document.querySelector('form[data-aksrt-search-form="inline"]');
|
||||
if (form) enhancePanel(form);
|
||||
})
|
||||
.catch(function () {
|
||||
box.removeAttribute("data-loaded");
|
||||
});
|
||||
}
|
||||
|
||||
function enhancePanel(form) {
|
||||
var group = form.querySelector(".form-group");
|
||||
var panel = form.querySelector(".navbar-inline-search-panel");
|
||||
var input = form.querySelector(".search-input");
|
||||
var group = form.querySelector(".form-group.relative, .form-group");
|
||||
if (!input || !group) return;
|
||||
if (!group || !input) return;
|
||||
|
||||
var panel = group.querySelector(".navbar-inline-search-panel");
|
||||
if (!panel) {
|
||||
panel = document.createElement("div");
|
||||
panel.className = "dropdown-menu hover-show-con navbar-inline-search-panel";
|
||||
group.appendChild(panel);
|
||||
// Merge guest local history into panel list.
|
||||
var histList = form.querySelector(".aksrt-menu-search-history__list");
|
||||
if (histList && !(window.aksrtData && aksrtData.isLoggedIn)) {
|
||||
var local = readLocalHistory();
|
||||
if (local.length) {
|
||||
histList.innerHTML = local
|
||||
.map(function (kw) {
|
||||
return (
|
||||
'<a class="but jb-sm" href="' +
|
||||
(window.location.origin || "") +
|
||||
"/?s=" +
|
||||
encodeURIComponent(kw) +
|
||||
'">' +
|
||||
kw +
|
||||
"</a> "
|
||||
);
|
||||
})
|
||||
.join("");
|
||||
}
|
||||
}
|
||||
|
||||
var clearBtn = form.querySelector("[data-aksrt-clear-menu-history]");
|
||||
if (clearBtn && !clearBtn._bound) {
|
||||
clearBtn._bound = 1;
|
||||
clearBtn.addEventListener("click", function (e) {
|
||||
e.preventDefault();
|
||||
writeLocalHistory([]);
|
||||
if (histList) {
|
||||
histList.innerHTML =
|
||||
'<span class="muted-3-color em09" data-empty="1">暂无历史</span>';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var suggestMount = form.querySelector("[data-aksrt-menu-suggest]");
|
||||
var timer = null;
|
||||
var searchConfig = (window.aksrtData && aksrtData.search) || {};
|
||||
var suggestionsEnabled = searchConfig.suggestionsEnabled !== false;
|
||||
|
||||
function typeValue() {
|
||||
var t = form.querySelector('input[name="aksrt_search_type"]');
|
||||
return (t && t.value) || searchConfig.defaultType || "post";
|
||||
}
|
||||
|
||||
function renderItems(items) {
|
||||
var html = "";
|
||||
if (items && items.length) {
|
||||
html += '<div class="muted-2-color em09 mb6">' + (searchConfig.suggestTitle || "搜索建议") + "</div>";
|
||||
html += '<div class="navbar-inline-search-suggest">';
|
||||
items.forEach(function (it) {
|
||||
html +=
|
||||
'<a class="navbar-inline-search-suggest__item" href="' +
|
||||
(it.url || "#") +
|
||||
'"><span class="title">' +
|
||||
(it.title || "") +
|
||||
'</span>' +
|
||||
(it.meta ? '<span class="meta muted-2-color em09">' + it.meta + "</span>" : "") +
|
||||
"</a>";
|
||||
});
|
||||
html += "</div>";
|
||||
}
|
||||
// Keep hot keywords block if present as static sibling content was replaced —
|
||||
// re-append hots from data attribute if we stored them.
|
||||
var hots = form.getAttribute("data-hots");
|
||||
if (hots) {
|
||||
try {
|
||||
var arr = JSON.parse(hots);
|
||||
if (arr && arr.length) {
|
||||
html += '<div class="muted-2-color em09 mb6 mt10">热门搜索</div><div class="navbar-inline-search-hots">';
|
||||
arr.forEach(function (kw) {
|
||||
html +=
|
||||
'<a class="but jb-sm" href="' +
|
||||
(window.location.origin || "") +
|
||||
"/?s=" +
|
||||
encodeURIComponent(kw) +
|
||||
'">' +
|
||||
kw +
|
||||
"</a> ";
|
||||
});
|
||||
html += "</div>";
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
panel.innerHTML = html || panel.innerHTML;
|
||||
panel.classList.add("is-open");
|
||||
panel.style.display = "block";
|
||||
panel.style.visibility = "visible";
|
||||
}
|
||||
|
||||
// Persist hot keywords for re-render after suggestions
|
||||
var hotLinks = panel.querySelectorAll(".navbar-inline-search-hots a");
|
||||
if (hotLinks.length) {
|
||||
var kws = [];
|
||||
hotLinks.forEach(function (a) {
|
||||
kws.push((a.textContent || "").trim());
|
||||
});
|
||||
form.setAttribute("data-hots", JSON.stringify(kws));
|
||||
}
|
||||
|
||||
function request(q) {
|
||||
if (!suggestionsEnabled || q.length < 2) {
|
||||
// restore hots-only
|
||||
if (form.getAttribute("data-hots")) {
|
||||
renderItems([]);
|
||||
function requestSuggest(q) {
|
||||
if (!suggestMount || !ajaxUrl() || q.length < 2) {
|
||||
if (suggestMount) {
|
||||
suggestMount.hidden = true;
|
||||
suggestMount.innerHTML = "";
|
||||
}
|
||||
return;
|
||||
}
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", aksrtData.ajaxUrl, true);
|
||||
xhr.open("POST", ajaxUrl(), true);
|
||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
|
||||
xhr.onload = function () {
|
||||
if (xhr.status < 200 || xhr.status >= 300) return;
|
||||
try {
|
||||
var json = JSON.parse(xhr.responseText);
|
||||
if (json.success && json.data && json.data.items) {
|
||||
renderItems(json.data.items);
|
||||
var items = (json.success && json.data && json.data.items) || [];
|
||||
if (!items.length) {
|
||||
suggestMount.hidden = true;
|
||||
suggestMount.innerHTML = "";
|
||||
return;
|
||||
}
|
||||
var html =
|
||||
'<div class="muted-2-color em09 mb6">' +
|
||||
(searchConfig.suggestTitle || "搜索建议") +
|
||||
"</div><div class=\"navbar-inline-search-suggest\">";
|
||||
items.forEach(function (it) {
|
||||
html +=
|
||||
'<a class="navbar-inline-search-suggest__item" href="' +
|
||||
(it.url || "#") +
|
||||
'"><span class="title">' +
|
||||
(it.title || "") +
|
||||
'</span>' +
|
||||
(it.meta
|
||||
? '<span class="meta muted-2-color em09">' + it.meta + "</span>"
|
||||
: "") +
|
||||
"</a>";
|
||||
});
|
||||
html += "</div>";
|
||||
suggestMount.innerHTML = html;
|
||||
suggestMount.hidden = false;
|
||||
} catch (e) {}
|
||||
};
|
||||
xhr.send(
|
||||
"action=aksrt_search_suggestions&nonce=" +
|
||||
encodeURIComponent(aksrtData.nonce) +
|
||||
encodeURIComponent(nonce()) +
|
||||
"&q=" +
|
||||
encodeURIComponent(q) +
|
||||
"&type=" +
|
||||
@@ -114,27 +168,75 @@
|
||||
);
|
||||
}
|
||||
|
||||
input.addEventListener("input", function () {
|
||||
var q = (input.value || "").trim();
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = setTimeout(function () {
|
||||
request(q);
|
||||
}, 180);
|
||||
if (!input._aksrtMenuSearchBound) {
|
||||
input._aksrtMenuSearchBound = 1;
|
||||
input.addEventListener("input", function () {
|
||||
var q = (input.value || "").trim();
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = setTimeout(function () {
|
||||
requestSuggest(q);
|
||||
}, 180);
|
||||
});
|
||||
input.addEventListener("focus", function () {
|
||||
var remote = form.querySelector("[data-aksrt-menu-search-remote]");
|
||||
if (remote) loadRemote(remote);
|
||||
var p = form.querySelector(".navbar-inline-search-panel");
|
||||
if (p) {
|
||||
p.classList.add("is-open");
|
||||
p.style.display = "block";
|
||||
p.style.visibility = "visible";
|
||||
}
|
||||
});
|
||||
form.addEventListener("submit", function () {
|
||||
var q = (input.value || "").trim();
|
||||
if (!q) return;
|
||||
if (!(window.aksrtData && aksrtData.isLoggedIn)) {
|
||||
var list = readLocalHistory().filter(function (k) {
|
||||
return k !== q;
|
||||
});
|
||||
list.unshift(q);
|
||||
writeLocalHistory(list);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
document.querySelectorAll("[data-aksrt-menu-search-remote]").forEach(function (box) {
|
||||
// Prefetch on idle / first hover of form.
|
||||
var form = box.closest("form");
|
||||
if (form) {
|
||||
form.addEventListener(
|
||||
"mouseenter",
|
||||
function () {
|
||||
loadRemote(box);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
// Also load when lazyload-action ias would fire — simple viewport/idle.
|
||||
if ("requestIdleCallback" in window) {
|
||||
requestIdleCallback(function () {
|
||||
loadRemote(box);
|
||||
});
|
||||
} else {
|
||||
setTimeout(function () {
|
||||
loadRemote(box);
|
||||
}, 600);
|
||||
}
|
||||
});
|
||||
|
||||
input.addEventListener("focus", function () {
|
||||
panel.style.display = "block";
|
||||
panel.style.visibility = "visible";
|
||||
});
|
||||
var form = document.querySelector('form[data-aksrt-search-form="inline"]');
|
||||
if (form) enhancePanel(form);
|
||||
}
|
||||
|
||||
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === "function") {
|
||||
window.AksrtFrontend.register("aksrt-inline-search-suggest", function () {
|
||||
initInlineSearchSuggest();
|
||||
init();
|
||||
});
|
||||
} else if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", initInlineSearchSuggest);
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
} else {
|
||||
initInlineSearchSuggest();
|
||||
init();
|
||||
}
|
||||
})();
|
||||
Reference in new issue
Block a user