🍉 Sync 2025-12-06 00:12:49
This commit is contained in:
@@ -67,6 +67,9 @@ msgstr "等级 4"
|
||||
msgid "Irqbalance"
|
||||
msgstr "中断均衡器"
|
||||
|
||||
msgid "Running Status"
|
||||
msgstr "运行状态"
|
||||
|
||||
msgid "<b><font color=\"green\">RUNNING</font></b>"
|
||||
msgstr "<b><font color=\"green\">运行中</font></b>"
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ function index()
|
||||
entry({"admin", "services", appname, "copy_node"}, call("copy_node")).leaf = true
|
||||
entry({"admin", "services", appname, "clear_all_nodes"}, call("clear_all_nodes")).leaf = true
|
||||
entry({"admin", "services", appname, "delete_select_nodes"}, call("delete_select_nodes")).leaf = true
|
||||
entry({"admin", "services", appname, "reassign_group"}, call("reassign_group")).leaf = true
|
||||
entry({"admin", "services", appname, "get_node"}, call("get_node")).leaf = true
|
||||
entry({"admin", "services", appname, "save_node_order"}, call("save_node_order")).leaf = true
|
||||
entry({"admin", "services", appname, "update_rules"}, call("update_rules")).leaf = true
|
||||
@@ -640,6 +641,20 @@ function save_node_order()
|
||||
http_write_json({ status = "ok" })
|
||||
end
|
||||
|
||||
function reassign_group()
|
||||
local ids = http.formvalue("ids") or ""
|
||||
local group = http.formvalue("group") or "default"
|
||||
for id in ids:gmatch("([^,]+)") do
|
||||
if group ~="" and group ~= "default" then
|
||||
api.sh_uci_set(appname, id, "group", group)
|
||||
else
|
||||
api.sh_uci_del(appname, id, "group")
|
||||
end
|
||||
end
|
||||
api.sh_uci_commit(appname)
|
||||
http_write_json({ status = "ok" })
|
||||
end
|
||||
|
||||
function update_rules()
|
||||
local update = http.formvalue("update")
|
||||
luci.sys.call("lua /usr/share/passwall/rule_update.lua log '" .. update .. "' > /dev/null 2>&1 &")
|
||||
|
||||
@@ -41,12 +41,16 @@ local api = require "luci.passwall.api"
|
||||
}
|
||||
|
||||
function open_add_link_div() {
|
||||
document.getElementById('modal-mask').style.display = 'block';
|
||||
document.getElementById("add_link_div").style.display = "block";
|
||||
document.body.classList.add('modal-open');
|
||||
document.getElementById("nodes_link").focus();
|
||||
}
|
||||
|
||||
function close_add_link_div() {
|
||||
document.getElementById('modal-mask').style.display = 'none';
|
||||
document.getElementById("add_link_div").style.display = "none";
|
||||
document.body.classList.remove('modal-open');
|
||||
}
|
||||
|
||||
function add_node() {
|
||||
@@ -81,13 +85,69 @@ local api = require "luci.passwall.api"
|
||||
}
|
||||
}
|
||||
|
||||
function open_reassign_group_div() {
|
||||
var ids = [];
|
||||
var visibleContainer = document.querySelector('#cbi-passwall-nodes > .cbi-tabcontainer[style*="display:block"], #cbi-passwall-nodes > .cbi-tabcontainer[style*="display: block"]');
|
||||
if (!visibleContainer) return;
|
||||
var doms = visibleContainer.getElementsByClassName("nodes_select");
|
||||
if (doms && doms.length > 0) {
|
||||
for (var i = 0 ; i < doms.length; i++) {
|
||||
if (doms[i].checked) {
|
||||
ids.push(doms[i].getAttribute("cbid"))
|
||||
}
|
||||
}
|
||||
if (ids.length > 0) {
|
||||
document.getElementById('modal-mask').style.display = 'block';
|
||||
document.getElementById("reassign_group_div").style.display = "block";
|
||||
document.body.classList.add('modal-open');
|
||||
} else {
|
||||
alert("<%:You no select nodes !%>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function close_reassign_group_div() {
|
||||
document.getElementById('modal-mask').style.display = 'none';
|
||||
document.getElementById("reassign_group_div").style.display = "none";
|
||||
document.body.classList.remove('modal-open');
|
||||
}
|
||||
|
||||
function reassign_group() {
|
||||
var ids = [];
|
||||
var group = (document.querySelector('#reassign_group_custom input[type="hidden"]')?.value || "default");
|
||||
var visibleContainer = document.querySelector('#cbi-passwall-nodes > .cbi-tabcontainer[style*="display:block"], #cbi-passwall-nodes > .cbi-tabcontainer[style*="display: block"]');
|
||||
if (!visibleContainer) return;
|
||||
var doms = visibleContainer.getElementsByClassName("nodes_select");
|
||||
if (doms && doms.length > 0) {
|
||||
for (var i = 0 ; i < doms.length; i++) {
|
||||
if (doms[i].checked) {
|
||||
ids.push(doms[i].getAttribute("cbid"))
|
||||
}
|
||||
}
|
||||
if (ids.length > 0) {
|
||||
XHR.get('<%=api.url("reassign_group")%>', {
|
||||
group: group,
|
||||
ids: ids.join(",")
|
||||
},
|
||||
function(x, data) {
|
||||
if (x && x.status == 200) {
|
||||
window.location.href = '<%=api.url("node_list")%>';
|
||||
}
|
||||
else {
|
||||
alert("<%:Error%>");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function add_new_node() {
|
||||
window.location.href = '<%=api.url("add_node")%>?redirect=1';
|
||||
}
|
||||
|
||||
//自定义分组下拉列表事件
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
var dropdown = document.getElementById("addlink_group_custom");
|
||||
function dropdown_list_fun(div_id) {
|
||||
var dropdown = document.getElementById(div_id);
|
||||
if (!dropdown) return;
|
||||
|
||||
var display = dropdown.querySelector(".selected-display");
|
||||
@@ -99,7 +159,7 @@ local api = require "luci.passwall.api"
|
||||
display.addEventListener("click", function() {
|
||||
list.style.display = list.style.display === "none" ? "block" : "none";
|
||||
input.value = "";
|
||||
input.focus();
|
||||
//input.focus();
|
||||
});
|
||||
|
||||
function selectItem(li) {
|
||||
@@ -119,7 +179,8 @@ local api = require "luci.passwall.api"
|
||||
});
|
||||
|
||||
input.addEventListener("keydown", function(e){
|
||||
if (e.keyCode !== 13) return;
|
||||
var isEnter = e.key === "Enter" || e.keyCode === 13;
|
||||
if (!isEnter) return;
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
@@ -135,7 +196,7 @@ local api = require "luci.passwall.api"
|
||||
}
|
||||
|
||||
var li = Array.from(list.querySelectorAll(".dropdown-item")).find(function(el){
|
||||
return el.dataset.value.toLowerCase() === val.toLowerCase();
|
||||
return el.dataset.value && el.dataset.value.toLowerCase() === val.toLowerCase();
|
||||
});
|
||||
if (!li) {
|
||||
li = document.createElement("li");
|
||||
@@ -176,11 +237,20 @@ local api = require "luci.passwall.api"
|
||||
list.style.display = "none";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
dropdown_list_fun("addlink_group_custom");
|
||||
});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
dropdown_list_fun("reassign_group_custom");
|
||||
});
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<div id="modal-mask"></div>
|
||||
|
||||
<div id="add_link_div">
|
||||
<div id="add_link_modal_container">
|
||||
<h3><%:Add the node via the link%></h3>
|
||||
@@ -199,7 +269,7 @@ local api = require "luci.passwall.api"
|
||||
<ul class="dropdown-list" style="display:none;">
|
||||
<li class="dropdown-item" data-value=""><%:default%></li>
|
||||
<li class="dropdown-item custom-input">
|
||||
<input type="text" placeholder="-- <%:custom%> --" class="create-item-input">
|
||||
<input type="text" placeholder="-- <%:custom%> --" class="create-item-input" inputmode="text" enterkeyhint="done">
|
||||
</li>
|
||||
</ul>
|
||||
<input type="hidden" name="addlink_group" value="">
|
||||
@@ -213,6 +283,36 @@ local api = require "luci.passwall.api"
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="reassign_group_div">
|
||||
<div id="reassign_group_modal_container">
|
||||
<h3><%:Reassign Node Group%></h3>
|
||||
<div class="value-custom">
|
||||
<div class="value-field-custom">
|
||||
<label class="value-title-custom" for="reassign_group_custom"><%:Group Name%></label>
|
||||
<div id="reassign_group_custom" class="custom-dropdown">
|
||||
<div class="selected-display">
|
||||
<span class="text"><%:default%></span>
|
||||
<span class="arrow">▾</span>
|
||||
</div>
|
||||
<ul class="dropdown-list" style="display:none;">
|
||||
<li class="dropdown-item" data-value=""><%:default%></li>
|
||||
<li class="dropdown-item custom-input">
|
||||
<input type="text" placeholder="-- <%:custom%> --" class="create-item-input" inputmode="text" enterkeyhint="done">
|
||||
</li>
|
||||
</ul>
|
||||
<input type="hidden" name="to_group" value="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="reassign_group_button_container">
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" onclick="reassign_group()" value="<%:Save%>" />
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="close_reassign_group_div()" value="<%:Close%>" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="pw-toolbar">
|
||||
<div class="pw-toolbar-field">
|
||||
<input class="btn cbi-button cbi-button-add" type="button" onclick="add_new_node()" value="<%:Add%>" />
|
||||
@@ -220,9 +320,10 @@ local api = require "luci.passwall.api"
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="clear_all_nodes()" value="<%:Clear all nodes%>" />
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="delete_select_nodes()" value="<%:Delete select nodes%>" />
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" id="select_all_btn" onclick="checked_all_node(this)" value="<%:Select all%>" />
|
||||
<input class="btn cbi-button cbi-button-apply" type="submit" name="cbi.apply" value="<%:Save & Apply%>" />
|
||||
<input class="btn cbi-button cbi-button-save" type="submit" name="cbi.save" value="<%:Save%>" />
|
||||
<input class="btn cbi-button cbi-button-reset" type="button" value="<%:Reset%>" onclick="location.href='<%=REQUEST_URI%>'" />
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" onclick="open_reassign_group_div()" value="<%:Reassign Group%>" />
|
||||
<!--<input class="btn cbi-button cbi-button-apply" type="submit" name="cbi.apply" value="<%:Save & Apply%>" />-->
|
||||
<!--<input class="btn cbi-button cbi-button-save" type="submit" name="cbi.save" value="<%:Save%>" />-->
|
||||
<!--<input class="btn cbi-button cbi-button-reset" type="button" value="<%:Reset%>" onclick="location.href='<%=REQUEST_URI%>'" />-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -239,7 +340,28 @@ local api = require "luci.passwall.api"
|
||||
padding: 5px 0 5px;
|
||||
}
|
||||
|
||||
#add_link_div {
|
||||
#modal-mask {
|
||||
display: none;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0,0,0,0.4);
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
body.modal-open {
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
body.modal-open #add_link_div,
|
||||
body.modal-open #reassign_group_div {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#add_link_div, #reassign_group_div {
|
||||
display: none;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
@@ -254,7 +376,7 @@ local api = require "luci.passwall.api"
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
#add_link_modal_container {
|
||||
#add_link_modal_container, #reassign_group_modal_container {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
@@ -281,7 +403,7 @@ local api = require "luci.passwall.api"
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#add_link_button_container {
|
||||
#add_link_button_container, #reassign_group_button_container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
|
||||
@@ -2005,5 +2005,11 @@ msgstr "配置的类型同样适用于手动导入节点时所指定的核心程
|
||||
msgid "Group Name"
|
||||
msgstr "分组名"
|
||||
|
||||
msgid "Reassign Group"
|
||||
msgstr "调整分组"
|
||||
|
||||
msgid "Reassign Node Group"
|
||||
msgstr "调整节点分组"
|
||||
|
||||
msgid "Currently using %s node"
|
||||
msgstr "当前使用的 %s 节点"
|
||||
|
||||
@@ -319,23 +319,19 @@
|
||||
@layer components {
|
||||
@layer layout {
|
||||
header {
|
||||
@apply sticky top-0 z-60 mb-2 transition-all duration-300 before:pointer-events-none before:absolute before:inset-0 before:-z-10 before:bg-white/10 before:backdrop-blur-xl before:backdrop-saturate-150 before:transition-all before:duration-300 dark:before:bg-gray-900/90;
|
||||
@apply sticky top-0 z-60 mb-2 transition-all duration-300 before:pointer-events-none before:absolute before:inset-0 before:-z-10 before:bg-white/20 before:backdrop-blur-xl before:backdrop-saturate-150 before:transition-all before:duration-300 dark:before:bg-gray-900/90;
|
||||
|
||||
& .header-content {
|
||||
@apply relative flex h-14 items-center justify-between px-6 py-3 max-md:px-4 max-md:py-2;
|
||||
}
|
||||
|
||||
& .desktop-menu-container {
|
||||
@apply pointer-events-none absolute inset-x-0 top-0 z-30 h-0 w-full overflow-hidden opacity-0 transition-all duration-150 ease-in-out before:pointer-events-none before:absolute before:inset-x-0 before:top-0 before:h-full before:min-h-full before:bg-white/40 before:backdrop-blur-xl before:backdrop-saturate-150 max-md:hidden dark:before:bg-gray-900/90;
|
||||
|
||||
&.active {
|
||||
@apply pointer-events-auto opacity-100;
|
||||
}
|
||||
}
|
||||
|
||||
&.has-desktop-nav {
|
||||
[data-nav-type="mega-menu"] & {
|
||||
& .desktop-menu-container {
|
||||
@apply pointer-events-auto opacity-100;
|
||||
@apply pointer-events-none absolute inset-x-0 top-0 z-30 h-0 w-full overflow-hidden opacity-0 transition-all duration-150 ease-in-out before:pointer-events-none before:absolute before:inset-x-0 before:top-0 before:h-full before:min-h-full before:bg-white/40 before:backdrop-blur-xl before:backdrop-saturate-150 max-md:hidden dark:before:bg-gray-900/90;
|
||||
|
||||
&.active {
|
||||
@apply pointer-events-auto opacity-100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,28 +346,51 @@
|
||||
@apply relative;
|
||||
|
||||
.menu {
|
||||
@apply block px-3.5 py-1.5 font-medium text-slate-700 no-underline transition-all duration-150 hover:text-slate-900 dark:text-slate-200 hover:dark:text-slate-100;
|
||||
@apply block rounded-xl px-3.5 py-1.5 font-medium text-slate-700 no-underline transition-all duration-150 dark:text-slate-200;
|
||||
|
||||
&.menu-active {
|
||||
@apply text-slate-900 underline decoration-2 underline-offset-4 dark:text-slate-100;
|
||||
@apply text-slate-900 dark:text-slate-100;
|
||||
[data-nav-type="mega-menu"] & {
|
||||
@apply underline decoration-2 underline-offset-4;
|
||||
}
|
||||
[data-nav-type="boxed-dropdown"] & {
|
||||
@apply bg-slate-200/90 dark:bg-slate-700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& .desktop-nav {
|
||||
@apply pointer-events-none fixed top-14 left-0 z-40 px-0 pt-0 pb-6 opacity-0 transition-all duration-150 max-md:hidden;
|
||||
|
||||
@apply pointer-events-none left-0 z-40 px-0 pt-0 opacity-0 transition-all duration-150;
|
||||
[data-nav-type="mega-menu"] & {
|
||||
@apply fixed top-14 pb-6;
|
||||
}
|
||||
[data-nav-type="boxed-dropdown"] & {
|
||||
@apply absolute top-full mt-2 min-w-48;
|
||||
}
|
||||
&.active {
|
||||
@apply pointer-events-auto opacity-100;
|
||||
}
|
||||
|
||||
& .desktop-nav-list {
|
||||
@apply grid w-max list-none grid-cols-4 gap-x-4 gap-y-2;
|
||||
[data-nav-type="mega-menu"] & {
|
||||
@apply grid w-max grid-cols-4 gap-x-4 gap-y-2;
|
||||
}
|
||||
|
||||
[data-nav-type="boxed-dropdown"] & {
|
||||
@apply flex flex-col gap-y-1 rounded-3xl border border-slate-200/70 bg-white/80 py-2 shadow-lg backdrop-blur-md backdrop-saturate-150 transition-all duration-200 dark:border-gray-700/70 dark:bg-gray-900/90;
|
||||
}
|
||||
& > li {
|
||||
@apply m-0 list-none;
|
||||
|
||||
& > a {
|
||||
@apply block px-4 py-3 whitespace-nowrap text-slate-700 no-underline hover:text-slate-900 hover:underline hover:decoration-2 hover:underline-offset-4 dark:text-slate-300 hover:dark:text-slate-100;
|
||||
@apply block px-4 whitespace-nowrap text-slate-700 no-underline hover:text-slate-900 dark:text-slate-300 hover:dark:text-slate-100;
|
||||
|
||||
[data-nav-type="mega-menu"] & {
|
||||
@apply py-3 hover:underline hover:decoration-2 hover:underline-offset-4;
|
||||
}
|
||||
[data-nav-type="boxed-dropdown"] & {
|
||||
@apply mx-2 rounded-2xl py-2 hover:bg-slate-200/60 dark:hover:bg-slate-700/70;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -940,6 +959,9 @@
|
||||
@layer progress {
|
||||
.cbi-progressbar {
|
||||
@apply relative h-3.5 w-full cursor-help overflow-hidden rounded-full bg-slate-300 before:absolute before:top-1/2 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2 before:rounded-2xl before:text-xs/normal before:whitespace-nowrap before:text-slate-900 before:content-[attr(title)] max-md:h-4 max-md:rounded-2xl max-md:before:text-xs max-md:before:leading-normal dark:bg-slate-700 before:dark:border-slate-600 before:dark:text-slate-100 dark:before:text-slate-100;
|
||||
[data-page="admin-system-package-manager"] & {
|
||||
@apply max-sm:before:text-[10px];
|
||||
}
|
||||
|
||||
& > div {
|
||||
@apply from-progress-start to-progress-end h-full bg-gradient-to-r transition-all duration-300;
|
||||
@@ -1422,11 +1444,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.desktop-menu-overlay {
|
||||
@apply pointer-events-none fixed inset-0 z-50 bg-black/20 opacity-0 transition-all duration-300 max-md:hidden;
|
||||
[data-nav-type="mega-menu"] {
|
||||
& .desktop-menu-overlay {
|
||||
@apply pointer-events-none fixed inset-0 z-50 bg-black/20 opacity-0 transition-all duration-300 max-md:hidden;
|
||||
|
||||
&.active {
|
||||
@apply pointer-events-auto opacity-100 backdrop-blur-sm;
|
||||
&.active {
|
||||
@apply pointer-events-auto opacity-100 backdrop-blur-sm;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,129 +214,12 @@ return baseclass.extend({
|
||||
if (!children.length || level > 1) return E([]);
|
||||
|
||||
if (level === 0) {
|
||||
const container = document.querySelector(".desktop-menu-container");
|
||||
const overlay = document.querySelector(".desktop-menu-overlay");
|
||||
const header = document.querySelector("header");
|
||||
const navType = document.body?.dataset?.navType || "mega-menu";
|
||||
|
||||
let showTimer = null;
|
||||
let hideTimer = null;
|
||||
|
||||
children.forEach((child) => {
|
||||
const submenu = ui.menu.getChildren(child);
|
||||
const hasSubmenu = submenu.length > 0;
|
||||
|
||||
const li = E(
|
||||
"li",
|
||||
{
|
||||
class: hasSubmenu ? "has-desktop-nav" : "",
|
||||
},
|
||||
[
|
||||
E(
|
||||
"a",
|
||||
{
|
||||
class: "menu",
|
||||
href: hasSubmenu ? "#" : L.url(url, child.name),
|
||||
},
|
||||
[_(child.title)],
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
ul.appendChild(li);
|
||||
|
||||
if (hasSubmenu) {
|
||||
const nav = E(
|
||||
"div",
|
||||
{
|
||||
class: "desktop-nav",
|
||||
},
|
||||
[this.renderMainMenu(child, `${url}/${child.name}`, level + 1)],
|
||||
);
|
||||
|
||||
li.appendChild(nav);
|
||||
|
||||
const menuLink = li.querySelector("a");
|
||||
|
||||
li.addEventListener("mouseenter", () => {
|
||||
if (hideTimer) {
|
||||
clearTimeout(hideTimer);
|
||||
hideTimer = null;
|
||||
}
|
||||
|
||||
showTimer = setTimeout(() => {
|
||||
const wasActive = nav.classList.contains("active");
|
||||
|
||||
document.querySelectorAll(".desktop-nav").forEach((n) => {
|
||||
if (n !== nav) n.classList.remove("active");
|
||||
});
|
||||
|
||||
document.querySelectorAll("#topmenu a").forEach((a) => {
|
||||
if (a !== menuLink) a.classList.remove("menu-active");
|
||||
});
|
||||
|
||||
if (wasActive) return;
|
||||
|
||||
menuLink.classList.add("menu-active");
|
||||
header.classList.add("has-desktop-nav");
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const navHeight = nav.scrollHeight;
|
||||
const headerHeight =
|
||||
header.querySelector(".header-content")?.offsetHeight || 56;
|
||||
const totalHeight = headerHeight + navHeight;
|
||||
|
||||
if (container) {
|
||||
container.style.height = `${totalHeight}px`;
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
nav.classList.add("active");
|
||||
overlay.classList.add("active");
|
||||
|
||||
if (container) {
|
||||
container.classList.add("active");
|
||||
}
|
||||
});
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
|
||||
li.addEventListener("mouseleave", () => {
|
||||
if (showTimer) {
|
||||
clearTimeout(showTimer);
|
||||
showTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
menuLink.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (header && overlay) {
|
||||
const hideMenu = () => {
|
||||
if (showTimer) {
|
||||
clearTimeout(showTimer);
|
||||
showTimer = null;
|
||||
}
|
||||
|
||||
hideTimer = setTimeout(() => {
|
||||
this.hideDesktopNav();
|
||||
}, 150);
|
||||
};
|
||||
|
||||
header.addEventListener("mouseleave", hideMenu);
|
||||
overlay.addEventListener("mouseenter", hideMenu);
|
||||
|
||||
header.addEventListener("mouseenter", () => {
|
||||
if (hideTimer) {
|
||||
clearTimeout(hideTimer);
|
||||
hideTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
overlay.addEventListener("click", () => this.hideDesktopNav());
|
||||
if (navType === "mega-menu") {
|
||||
this.initMegaMenu(children, url, ul);
|
||||
} else {
|
||||
this.initBoxedDropdown(children, url, ul);
|
||||
}
|
||||
} else {
|
||||
children.forEach((child) => {
|
||||
@@ -352,22 +235,228 @@ return baseclass.extend({
|
||||
return ul;
|
||||
},
|
||||
|
||||
initMegaMenu(children, url, ul) {
|
||||
const container = document.querySelector(".desktop-menu-container");
|
||||
const overlay = document.querySelector(".desktop-menu-overlay");
|
||||
const header = document.querySelector("header");
|
||||
|
||||
if (!header || !overlay) return;
|
||||
|
||||
let showTimer = null;
|
||||
let hideTimer = null;
|
||||
|
||||
children.forEach((child) => {
|
||||
const submenu = ui.menu.getChildren(child);
|
||||
const hasSubmenu = submenu.length > 0;
|
||||
|
||||
const li = E(
|
||||
"li",
|
||||
{
|
||||
class: hasSubmenu ? "has-desktop-nav" : "",
|
||||
},
|
||||
[
|
||||
E(
|
||||
"a",
|
||||
{
|
||||
class: "menu",
|
||||
href: hasSubmenu ? "#" : L.url(url, child.name),
|
||||
},
|
||||
[_(child.title)],
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
ul.appendChild(li);
|
||||
|
||||
if (hasSubmenu) {
|
||||
const nav = E(
|
||||
"div",
|
||||
{
|
||||
class: "desktop-nav",
|
||||
},
|
||||
[this.renderMainMenu(child, `${url}/${child.name}`, 1)],
|
||||
);
|
||||
|
||||
li.appendChild(nav);
|
||||
|
||||
const menuLink = li.querySelector("a");
|
||||
|
||||
li.addEventListener("mouseenter", () => {
|
||||
if (hideTimer) {
|
||||
clearTimeout(hideTimer);
|
||||
hideTimer = null;
|
||||
}
|
||||
|
||||
showTimer = setTimeout(() => {
|
||||
const wasActive = nav.classList.contains("active");
|
||||
|
||||
document.querySelectorAll(".desktop-nav").forEach((n) => {
|
||||
if (n !== nav) n.classList.remove("active");
|
||||
});
|
||||
|
||||
document.querySelectorAll("#topmenu a").forEach((a) => {
|
||||
if (a !== menuLink) a.classList.remove("menu-active");
|
||||
});
|
||||
|
||||
if (wasActive) return;
|
||||
|
||||
menuLink.classList.add("menu-active");
|
||||
header.classList.add("has-desktop-nav");
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const navHeight = nav.scrollHeight;
|
||||
const headerHeight =
|
||||
header.querySelector(".header-content")?.offsetHeight || 56;
|
||||
const totalHeight = headerHeight + navHeight;
|
||||
|
||||
if (container) {
|
||||
container.style.height = `${totalHeight}px`;
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
nav.classList.add("active");
|
||||
overlay.classList.add("active");
|
||||
|
||||
if (container) {
|
||||
container.classList.add("active");
|
||||
}
|
||||
});
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
|
||||
li.addEventListener("mouseleave", () => {
|
||||
if (showTimer) {
|
||||
clearTimeout(showTimer);
|
||||
showTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
menuLink.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const hideMenu = () => {
|
||||
if (showTimer) {
|
||||
clearTimeout(showTimer);
|
||||
showTimer = null;
|
||||
}
|
||||
|
||||
hideTimer = setTimeout(() => {
|
||||
this.hideDesktopNav();
|
||||
}, 150);
|
||||
};
|
||||
|
||||
header.addEventListener("mouseleave", hideMenu);
|
||||
overlay.addEventListener("mouseenter", hideMenu);
|
||||
|
||||
header.addEventListener("mouseenter", () => {
|
||||
if (hideTimer) {
|
||||
clearTimeout(hideTimer);
|
||||
hideTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
overlay.addEventListener("click", () => this.hideDesktopNav());
|
||||
},
|
||||
|
||||
initBoxedDropdown(children, url, ul) {
|
||||
children.forEach((child) => {
|
||||
const submenu = ui.menu.getChildren(child);
|
||||
const hasSubmenu = submenu.length > 0;
|
||||
|
||||
const li = E("li", {}, [
|
||||
E(
|
||||
"a",
|
||||
{
|
||||
class: "menu",
|
||||
href: hasSubmenu ? "#" : L.url(url, child.name),
|
||||
},
|
||||
[_(child.title)],
|
||||
),
|
||||
]);
|
||||
|
||||
ul.appendChild(li);
|
||||
|
||||
if (hasSubmenu) {
|
||||
const nav = E(
|
||||
"div",
|
||||
{
|
||||
class: "desktop-nav",
|
||||
},
|
||||
[this.renderMainMenu(child, `${url}/${child.name}`, 1)],
|
||||
);
|
||||
|
||||
li.appendChild(nav);
|
||||
|
||||
const menuLink = li.querySelector("a");
|
||||
let showTimer = null;
|
||||
let hideTimer = null;
|
||||
|
||||
li.addEventListener("mouseenter", () => {
|
||||
if (hideTimer) {
|
||||
clearTimeout(hideTimer);
|
||||
hideTimer = null;
|
||||
}
|
||||
|
||||
showTimer = setTimeout(() => {
|
||||
document.querySelectorAll(".desktop-nav").forEach((n) => {
|
||||
if (n !== nav) n.classList.remove("active");
|
||||
});
|
||||
|
||||
document.querySelectorAll("#topmenu a").forEach((a) => {
|
||||
if (a !== menuLink) a.classList.remove("menu-active");
|
||||
});
|
||||
|
||||
menuLink.classList.add("menu-active");
|
||||
nav.classList.add("active");
|
||||
}, 100);
|
||||
});
|
||||
|
||||
li.addEventListener("mouseleave", () => {
|
||||
if (showTimer) {
|
||||
clearTimeout(showTimer);
|
||||
showTimer = null;
|
||||
}
|
||||
|
||||
hideTimer = setTimeout(() => {
|
||||
nav.classList.remove("active");
|
||||
menuLink.classList.remove("menu-active");
|
||||
}, 150);
|
||||
});
|
||||
|
||||
menuLink.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
hideDesktopNav() {
|
||||
const navType = document.body?.dataset?.navType || "mega-menu";
|
||||
|
||||
document
|
||||
.querySelectorAll(".desktop-nav")
|
||||
.forEach((nav) => nav.classList.remove("active"));
|
||||
document.querySelector("header")?.classList.remove("has-desktop-nav");
|
||||
|
||||
const container = document.querySelector(".desktop-menu-container");
|
||||
if (container) {
|
||||
container.classList.remove("active");
|
||||
container.style.height = "";
|
||||
}
|
||||
|
||||
document.querySelector(".desktop-menu-overlay")?.classList.remove("active");
|
||||
document
|
||||
.querySelectorAll("#topmenu a")
|
||||
.forEach((a) => a.classList.remove("menu-active"));
|
||||
|
||||
if (navType === "mega-menu") {
|
||||
document.querySelector("header")?.classList.remove("has-desktop-nav");
|
||||
|
||||
const container = document.querySelector(".desktop-menu-container");
|
||||
if (container) {
|
||||
container.classList.remove("active");
|
||||
container.style.height = "";
|
||||
}
|
||||
|
||||
document
|
||||
.querySelector(".desktop-menu-overlay")
|
||||
?.classList.remove("active");
|
||||
}
|
||||
},
|
||||
|
||||
renderModeMenu(tree) {
|
||||
|
||||
@@ -8,8 +8,8 @@ include $(TOPDIR)/rules.mk
|
||||
LUCI_TITLE:=Aurora Theme (A modern browser theme built with Vite and Tailwind CSS)
|
||||
LUCI_DEPENDS:=+luci-base
|
||||
|
||||
PKG_VERSION:=0.8.8_beta
|
||||
PKG_RELEASE:=20251204
|
||||
PKG_VERSION:=0.8.10_beta
|
||||
PKG_RELEASE:=20251205
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
|
||||
LUCI_MINIFY_CSS:=
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -16,6 +16,7 @@
|
||||
const boardinfo = ubus.call('system', 'board');
|
||||
const uci = cursor();
|
||||
const tokens = uci.get_all('aurora', 'theme') || {};
|
||||
const nav_submenu_type = tokens.nav_submenu_type || 'mega-menu';
|
||||
|
||||
http.prepare_content('text/html; charset=UTF-8');
|
||||
-%}
|
||||
@@ -92,7 +93,7 @@
|
||||
<script src="{{ resource }}/cbi.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="lang_{{ dispatcher.lang }} {{ entityencode(striptags(node?.title ?? ''), true) }}" data-page="{{ entityencode(join('-', ctx.request_path), true) }}">
|
||||
<body class="lang_{{ dispatcher.lang }} {{ entityencode(striptags(node?.title ?? ''), true) }}" data-page="{{ entityencode(join('-', ctx.request_path), true) }}" data-nav-type="{{ nav_submenu_type }}">
|
||||
{% if (!blank_page): %}
|
||||
<header>
|
||||
<div class="header-content">
|
||||
@@ -108,12 +109,16 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="indicators" class="pull-right"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if (nav_submenu_type == 'mega-menu'): %}
|
||||
<div class="desktop-menu-container"></div>
|
||||
</header>
|
||||
|
||||
<div class="desktop-menu-overlay"></div>
|
||||
{% else %}
|
||||
</header>
|
||||
{% endif %}
|
||||
|
||||
<div class="mobile-menu-overlay" id="mobile-menu-overlay">
|
||||
<nav class="mobile-nav">
|
||||
|
||||
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
|
||||
PKG_NAME:=unishare
|
||||
PKG_VERSION:=1.1.1
|
||||
PKG_VERSION:=1.1.2
|
||||
PKG_RELEASE:=1
|
||||
PKG_MAINTAINER:=jjm2473 <jjm2473@gmail.com>
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ START=97
|
||||
STOP=05
|
||||
USE_PROCD=1
|
||||
|
||||
NEWLINE=$'\n'
|
||||
|
||||
# $section $proto callback $args
|
||||
validate_share() {
|
||||
local path
|
||||
@@ -137,13 +139,13 @@ add_webdav_share() {
|
||||
local v
|
||||
for u in $rw; do
|
||||
var="UNISHARE_DAV_${u}_rw"
|
||||
eval "v=\$$var"
|
||||
export -n "$var=${v:+$v }$name"
|
||||
eval "v=\"\$$var\""
|
||||
export -n "$var=${v}$name$NEWLINE"
|
||||
done
|
||||
for u in $ro; do
|
||||
var="UNISHARE_DAV_${u}_ro"
|
||||
eval "v=\$$var"
|
||||
export -n "$var=${v:+$v }$name"
|
||||
eval "v=\"\$$var\""
|
||||
export -n "$var=${v}$name$NEWLINE"
|
||||
done
|
||||
ln -s "$path" "/var/run/unishare/root/$name"
|
||||
}
|
||||
@@ -167,7 +169,9 @@ add_webdav_user() {
|
||||
echo " rules:"
|
||||
local v
|
||||
local r
|
||||
eval "v=\$UNISHARE_DAV_${username}_rw"
|
||||
local oifs=$IFS
|
||||
IFS="$NEWLINE"
|
||||
eval "v=\"\$UNISHARE_DAV_${username}_rw\""
|
||||
for r in $v; do
|
||||
echo " - path: /$r"
|
||||
echo " modify: true"
|
||||
@@ -176,7 +180,7 @@ add_webdav_user() {
|
||||
echo " - path: /$r"
|
||||
echo " modify: true"
|
||||
done
|
||||
eval "v=\$UNISHARE_DAV_${username}_ro"
|
||||
eval "v=\"\$UNISHARE_DAV_${username}_ro\""
|
||||
for r in $v; do
|
||||
echo " - path: /$r"
|
||||
echo " allow: true"
|
||||
@@ -185,6 +189,7 @@ add_webdav_user() {
|
||||
echo " - path: /$r"
|
||||
echo " allow: true"
|
||||
done
|
||||
IFS=$oifs
|
||||
}
|
||||
|
||||
config_webdav_header() {
|
||||
@@ -209,6 +214,8 @@ scope: /var/run/unishare/root
|
||||
modify: false
|
||||
rules:
|
||||
EOF
|
||||
local oifs=$IFS
|
||||
IFS="$NEWLINE"
|
||||
for r in $UNISHARE_DAV_everyone_rw; do
|
||||
echo " - path: /$r"
|
||||
echo " modify: true"
|
||||
@@ -217,6 +224,7 @@ EOF
|
||||
echo " - path: /$r"
|
||||
echo " allow: true"
|
||||
done
|
||||
IFS=$oifs
|
||||
cat <<-EOF
|
||||
- regex: true
|
||||
allow: false
|
||||
|
||||
Reference in New Issue
Block a user