mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-08-28 09:11:06 +00:00
fix: stabilize custom sidebar active state
This commit is contained in:
+23
-15
@@ -704,20 +704,23 @@ html.bsplus-custom-title-pending #title > :not(#bsplus-title-root),
|
|||||||
|
|
||||||
/* Native list may keep `.active` after click-through; force our rows clickable,
|
/* Native list may keep `.active` after click-through; force our rows clickable,
|
||||||
then lock siblings only while a custom drill `.sub` is open. */
|
then lock siblings only while a custom drill `.sub` is open. */
|
||||||
> #bsplus-sidebar-root > li.item {
|
> #bsplus-sidebar-root .bsplus-sidebar-item {
|
||||||
pointer-events: auto !important;
|
|
||||||
width: auto !important;
|
width: auto !important;
|
||||||
max-width: none !important;
|
max-width: none !important;
|
||||||
margin: 0 6px 8px !important;
|
margin: 0 6px 8px !important;
|
||||||
box-sizing: border-box !important;
|
box-sizing: border-box !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:has(> #bsplus-sidebar-root > li.hasChildren.active > .sub)
|
> #bsplus-sidebar-root > li.item {
|
||||||
|
pointer-events: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:has(> #bsplus-sidebar-root > li.hasChildren.bsplus-active > .sub)
|
||||||
> #bsplus-sidebar-root
|
> #bsplus-sidebar-root
|
||||||
> li:not(.hasChildren.active),
|
> li:not(.hasChildren.bsplus-active),
|
||||||
&:has(> #bsplus-sidebar-root > li.hasChildren.active > .sub)
|
&:has(> #bsplus-sidebar-root > li.hasChildren.bsplus-active > .sub)
|
||||||
> #bsplus-sidebar-root
|
> #bsplus-sidebar-root
|
||||||
> li:not(.hasChildren.active)
|
> li:not(.hasChildren.bsplus-active)
|
||||||
* {
|
* {
|
||||||
pointer-events: none !important;
|
pointer-events: none !important;
|
||||||
}
|
}
|
||||||
@@ -733,17 +736,21 @@ html.bsplus-custom-title-pending #title > :not(#bsplus-title-root),
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
> #bsplus-sidebar-root li.hasChildren.active > .sub {
|
> #bsplus-sidebar-root li.hasChildren.bsplus-active > .sub {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
|
transform: none;
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
background-image: none !important;
|
background-image: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
> #bsplus-sidebar-root li.hasChildren.active > .sub:has(.hasChildren.active) {
|
> #bsplus-sidebar-root li.hasChildren.bsplus-active
|
||||||
|
> .sub:has(.hasChildren.bsplus-active) {
|
||||||
pointer-events: none !important;
|
pointer-events: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
> #bsplus-sidebar-root li.hasChildren.active .hasChildren.active > .sub {
|
> #bsplus-sidebar-root li.hasChildren.bsplus-active
|
||||||
|
.hasChildren.bsplus-active
|
||||||
|
> .sub {
|
||||||
pointer-events: auto !important;
|
pointer-events: auto !important;
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
background-image: none !important;
|
background-image: none !important;
|
||||||
@@ -765,17 +772,17 @@ html.bsplus-custom-title-pending #title > :not(#bsplus-title-root),
|
|||||||
background-image: none !important;
|
background-image: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
> #bsplus-sidebar-root > li.hasChildren.active > label,
|
> #bsplus-sidebar-root > li.hasChildren.bsplus-active > label,
|
||||||
> #bsplus-sidebar-root.drilling > li.hasChildren > label {
|
> #bsplus-sidebar-root.drilling > li.hasChildren > label {
|
||||||
pointer-events: none !important;
|
pointer-events: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Theme decorations on root leaves (Beach palm/sand) must not show through
|
/* Theme decorations on root leaves (Beach palm/sand) must not show through
|
||||||
the transparent drill `.sub` panel. */
|
the transparent drill `.sub` panel. */
|
||||||
> #bsplus-sidebar-root:has(> li.hasChildren.active > .sub)
|
> #bsplus-sidebar-root:has(> li.hasChildren.bsplus-active > .sub)
|
||||||
> li:not(.hasChildren.active)::before,
|
> li:not(.hasChildren.bsplus-active)::before,
|
||||||
> #bsplus-sidebar-root:has(> li.hasChildren.active > .sub)
|
> #bsplus-sidebar-root:has(> li.hasChildren.bsplus-active > .sub)
|
||||||
> li:not(.hasChildren.active)::after {
|
> li:not(.hasChildren.bsplus-active)::after {
|
||||||
content: none !important;
|
content: none !important;
|
||||||
display: none !important;
|
display: none !important;
|
||||||
animation: none !important;
|
animation: none !important;
|
||||||
@@ -812,8 +819,9 @@ html.bsplus-custom-title-pending #title > :not(#bsplus-title-root),
|
|||||||
#bsplus-sidebar-root .sub > ul {
|
#bsplus-sidebar-root .sub > ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0 0 16px;
|
padding: 8px 0 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu li > label > svg,
|
#menu li > label > svg,
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
#menu ul:has(li.hasChildren.active) li.hasChildren.active {
|
#menu ul:has(li.hasChildren:is(.active, .bsplus-active))
|
||||||
|
li.hasChildren:is(.active, .bsplus-active) {
|
||||||
box-shadow: inset 0px 0 var(--item-colour, transparent) !important;
|
box-shadow: inset 0px 0 var(--item-colour, transparent) !important;
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu ul:not(:has(li.hasChildren.active)) {
|
#menu ul:not(:has(li.hasChildren:is(.active, .bsplus-active))) {
|
||||||
overflow-y: scroll !important;
|
overflow-y: scroll !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu > ul:has(li.hasChildren.active) > li::before,
|
#menu > ul:has(li.hasChildren:is(.active, .bsplus-active)) > li::before,
|
||||||
#menu > ul:has(li.hasChildren.active) > li::after,
|
#menu > ul:has(li.hasChildren:is(.active, .bsplus-active)) > li::after,
|
||||||
#menu > ul ul:has(li.hasChildren.active) > li::before,
|
#menu > ul ul:has(li.hasChildren:is(.active, .bsplus-active)) > li::before,
|
||||||
#menu > ul ul:has(li.hasChildren.active) > li::after,
|
#menu > ul ul:has(li.hasChildren:is(.active, .bsplus-active)) > li::after,
|
||||||
#menu > ul:has(li.hasChildren.active) > li > label,
|
#menu > ul:has(li.hasChildren:is(.active, .bsplus-active)) > li > label,
|
||||||
#menu > ul:has(li.hasChildren.active) > li > svg,
|
#menu > ul:has(li.hasChildren:is(.active, .bsplus-active)) > li > svg,
|
||||||
#menu > ul ul:has(li.hasChildren.active) > li > label,
|
#menu > ul ul:has(li.hasChildren:is(.active, .bsplus-active)) > li > label,
|
||||||
#menu > ul ul:has(li.hasChildren.active) > li > svg {
|
#menu > ul ul:has(li.hasChildren:is(.active, .bsplus-active)) > li > svg {
|
||||||
|
/* !important beats theme animations (e.g. Beach palm-slide forwards). */
|
||||||
transform: translateX(-320px) !important;
|
transform: translateX(-320px) !important;
|
||||||
animation: none !important;
|
animation: none !important;
|
||||||
}
|
}
|
||||||
@@ -28,12 +30,13 @@
|
|||||||
transition: transform 0.3s ease, left 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: transform 0.3s ease, left 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu > ul:has(li.hasChildren.active) > li.active {
|
#menu > ul:has(li.hasChildren:is(.active, .bsplus-active))
|
||||||
|
> li:is(.active, .bsplus-active) {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu li.hasChildren:not(.active) li .sub {
|
#menu li.hasChildren:not(:is(.active, .bsplus-active)) li .sub {
|
||||||
display: none;
|
display: none; // improves performance
|
||||||
}
|
}
|
||||||
|
|
||||||
.sub .sub {
|
.sub .sub {
|
||||||
@@ -41,16 +44,42 @@
|
|||||||
padding-top: 2px;
|
padding-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sub:has(ul > li.hasChildren.active) > .nav > .back {
|
.sub:has(ul > li.hasChildren:is(.active, .bsplus-active)) > .nav > .back {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.icon-only-sidebar:not(:has(#menu li.hasChildren.active)) #menu > ul:has(li.hasChildren.active) > li::before,
|
/* Icon-only collapsed: submenu slides over narrow icons */
|
||||||
body.icon-only-sidebar:not(:has(#menu li.hasChildren.active)) #menu > ul ul:has(li.hasChildren.active) > li::before,
|
body.icon-only-sidebar:not(:has(#menu li.hasChildren:is(.active, .bsplus-active)))
|
||||||
body.icon-only-sidebar:not(:has(#menu li.hasChildren.active)) #menu > ul:has(li.hasChildren.active) > li > label,
|
#menu
|
||||||
body.icon-only-sidebar:not(:has(#menu li.hasChildren.active)) #menu > ul:has(li.hasChildren.active) > li > svg,
|
> ul:has(li.hasChildren:is(.active, .bsplus-active))
|
||||||
body.icon-only-sidebar:not(:has(#menu li.hasChildren.active)) #menu > ul ul:has(li.hasChildren.active) > li > label,
|
> li::before,
|
||||||
body.icon-only-sidebar:not(:has(#menu li.hasChildren.active)) #menu > ul ul:has(li.hasChildren.active) > li > svg {
|
body.icon-only-sidebar:not(:has(#menu li.hasChildren:is(.active, .bsplus-active)))
|
||||||
|
#menu
|
||||||
|
> ul
|
||||||
|
ul:has(li.hasChildren:is(.active, .bsplus-active))
|
||||||
|
> li::before,
|
||||||
|
body.icon-only-sidebar:not(:has(#menu li.hasChildren:is(.active, .bsplus-active)))
|
||||||
|
#menu
|
||||||
|
> ul:has(li.hasChildren:is(.active, .bsplus-active))
|
||||||
|
> li
|
||||||
|
> label,
|
||||||
|
body.icon-only-sidebar:not(:has(#menu li.hasChildren:is(.active, .bsplus-active)))
|
||||||
|
#menu
|
||||||
|
> ul:has(li.hasChildren:is(.active, .bsplus-active))
|
||||||
|
> li
|
||||||
|
> svg,
|
||||||
|
body.icon-only-sidebar:not(:has(#menu li.hasChildren:is(.active, .bsplus-active)))
|
||||||
|
#menu
|
||||||
|
> ul
|
||||||
|
ul:has(li.hasChildren:is(.active, .bsplus-active))
|
||||||
|
> li
|
||||||
|
> label,
|
||||||
|
body.icon-only-sidebar:not(:has(#menu li.hasChildren:is(.active, .bsplus-active)))
|
||||||
|
#menu
|
||||||
|
> ul
|
||||||
|
ul:has(li.hasChildren:is(.active, .bsplus-active))
|
||||||
|
> li
|
||||||
|
> svg {
|
||||||
transform: translateX(-70px);
|
transform: translateX(-70px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ $sidebar-motion: 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|||||||
}
|
}
|
||||||
|
|
||||||
#menu.bsplus-custom-sidebar {
|
#menu.bsplus-custom-sidebar {
|
||||||
$item: "> #bsplus-sidebar-root > li.item:not(.bsplus-sidebar-edit-header):not(.bsplus-sidebar-edit-actions)";
|
$item: "> #bsplus-sidebar-root .bsplus-sidebar-item";
|
||||||
$label: "> #bsplus-sidebar-root > li.item > label:not(.toggle)";
|
$label: "> #bsplus-sidebar-root .bsplus-sidebar-item > label:not(.toggle)";
|
||||||
$active: "> #bsplus-sidebar-root > li.item.active:not(.hasChildren)";
|
$active: "> #bsplus-sidebar-root .bsplus-sidebar-item.bsplus-active:not(.hasChildren)";
|
||||||
|
|
||||||
/* Look: base radius (pill / sharp / strip override).
|
/* Look: base radius (pill / sharp / strip override).
|
||||||
Density/radius animate with the same easing as sidebar width. */
|
Density/radius animate with the same easing as sidebar width. */
|
||||||
@@ -37,6 +37,7 @@ $sidebar-motion: 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|||||||
}
|
}
|
||||||
|
|
||||||
#{$label} {
|
#{$label} {
|
||||||
|
min-height: 28px !important;
|
||||||
transition:
|
transition:
|
||||||
font-size $sidebar-motion,
|
font-size $sidebar-motion,
|
||||||
line-height $sidebar-motion,
|
line-height $sidebar-motion,
|
||||||
@@ -147,7 +148,7 @@ $sidebar-motion: 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
font-size: 13px !important;
|
font-size: 13px !important;
|
||||||
line-height: 1.2 !important;
|
line-height: 1.2 !important;
|
||||||
min-height: 0 !important;
|
min-height: 20px !important;
|
||||||
height: auto !important;
|
height: auto !important;
|
||||||
white-space: nowrap !important;
|
white-space: nowrap !important;
|
||||||
|
|
||||||
@@ -169,6 +170,7 @@ $sidebar-motion: 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
font-size: 17px !important;
|
font-size: 17px !important;
|
||||||
line-height: 1.25 !important;
|
line-height: 1.25 !important;
|
||||||
|
min-height: 30px !important;
|
||||||
|
|
||||||
> svg {
|
> svg {
|
||||||
width: 30px !important;
|
width: 30px !important;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { getNativeMenuList } from "@/seqta/ui/sidebar/parseNativeMenu";
|
||||||
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
|
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
|
||||||
import { waitForElm } from "@/seqta/utils/waitForElm";
|
import { waitForElm } from "@/seqta/utils/waitForElm";
|
||||||
import { renderAnalyticsPage } from "./ui";
|
import { renderAnalyticsPage } from "./ui";
|
||||||
@@ -23,10 +24,11 @@ export async function loadAnalyticsPage(): Promise<void> {
|
|||||||
async function loadAnalyticsPageInner(): Promise<void> {
|
async function loadAnalyticsPageInner(): Promise<void> {
|
||||||
document.title = "Analytics ― SEQTA Learn";
|
document.title = "Analytics ― SEQTA Learn";
|
||||||
|
|
||||||
document.querySelectorAll("#menu .item").forEach((item) => {
|
const menu = getNativeMenuList();
|
||||||
|
menu?.querySelectorAll(".item").forEach((item) => {
|
||||||
item.classList.remove("active");
|
item.classList.remove("active");
|
||||||
});
|
});
|
||||||
document.querySelector('[data-key="analytics"]')?.classList.add("active");
|
menu?.querySelector('[data-key="analytics"]')?.classList.add("active");
|
||||||
|
|
||||||
let main: HTMLElement;
|
let main: HTMLElement;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -2,10 +2,7 @@
|
|||||||
import { onDestroy } from "svelte";
|
import { onDestroy } from "svelte";
|
||||||
import Sortable from "sortablejs";
|
import Sortable from "sortablejs";
|
||||||
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
|
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
|
||||||
import {
|
import { sidebarState } from "./sidebarState.svelte";
|
||||||
restoreCustomMenuActive,
|
|
||||||
sidebarState,
|
|
||||||
} from "./sidebarState.svelte";
|
|
||||||
import SidebarItem from "./SidebarItem.svelte";
|
import SidebarItem from "./SidebarItem.svelte";
|
||||||
import type {
|
import type {
|
||||||
SidebarDrillFrame,
|
SidebarDrillFrame,
|
||||||
@@ -102,7 +99,7 @@
|
|||||||
`${Math.round(width)}px`,
|
`${Math.round(width)}px`,
|
||||||
);
|
);
|
||||||
// Fallback clone is created just after onStart; pin size + drop
|
// Fallback clone is created just after onStart; pin size + drop
|
||||||
// `.active` so theme/active rules don't expand it to full width.
|
// Active styling makes fallback clones wider; keep the source width.
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
const dragEl = document.querySelector(
|
const dragEl = document.querySelector(
|
||||||
".bsplus-sortable-drag",
|
".bsplus-sortable-drag",
|
||||||
@@ -110,7 +107,7 @@
|
|||||||
if (!dragEl) return;
|
if (!dragEl) return;
|
||||||
dragEl.style.width = `${Math.round(width)}px`;
|
dragEl.style.width = `${Math.round(width)}px`;
|
||||||
dragEl.style.maxWidth = `${Math.round(width)}px`;
|
dragEl.style.maxWidth = `${Math.round(width)}px`;
|
||||||
dragEl.classList.remove("active");
|
dragEl.classList.remove("bsplus-active");
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onEnd: (evt) => {
|
onEnd: (evt) => {
|
||||||
@@ -156,15 +153,6 @@
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// SEQTA strips `.active` from `#menu li` after clicks — re-apply from state.
|
|
||||||
// Do NOT MutationObserver class changes here: restore writes `.active`, SEQTA
|
|
||||||
// strips it again, and the feedback loop freezes the tab.
|
|
||||||
$effect(() => {
|
|
||||||
void sidebarState.activeKey;
|
|
||||||
void sidebarState.isDrilling;
|
|
||||||
restoreCustomMenuActive();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Drill `.sub` is position:absolute inside this scrollport — if the list was
|
// Drill `.sub` is position:absolute inside this scrollport — if the list was
|
||||||
// scrolled down (e.g. Folios/Goals near the bottom), the panel sits under the
|
// scrolled down (e.g. Folios/Goals near the bottom), the panel sits under the
|
||||||
// logo until we reset. Also reset when going back up the stack.
|
// logo until we reset. Also reset when going back up the stack.
|
||||||
@@ -269,7 +257,7 @@
|
|||||||
{@const folder = current.folder}
|
{@const folder = current.folder}
|
||||||
<li
|
<li
|
||||||
class="item hasChildren bsplus-sidebar-item"
|
class="item hasChildren bsplus-sidebar-item"
|
||||||
class:active={true}
|
class:bsplus-active={true}
|
||||||
data-key={current.frame.key}
|
data-key={current.frame.key}
|
||||||
data-path={folder?.path ?? undefined}
|
data-path={folder?.path ?? undefined}
|
||||||
style:--item-colour={folder?.itemColour || undefined}
|
style:--item-colour={folder?.itemColour || undefined}
|
||||||
|
|||||||
@@ -29,17 +29,18 @@
|
|||||||
const GRIP_SVG = `<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><circle cx="5" cy="3" r="1.35" fill="currentColor"/><circle cx="11" cy="3" r="1.35" fill="currentColor"/><circle cx="5" cy="8" r="1.35" fill="currentColor"/><circle cx="11" cy="8" r="1.35" fill="currentColor"/><circle cx="5" cy="13" r="1.35" fill="currentColor"/><circle cx="11" cy="13" r="1.35" fill="currentColor"/></svg>`;
|
const GRIP_SVG = `<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><circle cx="5" cy="3" r="1.35" fill="currentColor"/><circle cx="11" cy="3" r="1.35" fill="currentColor"/><circle cx="5" cy="8" r="1.35" fill="currentColor"/><circle cx="11" cy="8" r="1.35" fill="currentColor"/><circle cx="5" cy="13" r="1.35" fill="currentColor"/><circle cx="11" cy="13" r="1.35" fill="currentColor"/></svg>`;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- SEQTA class names (item / hasChildren / active) so theme CSS keeps matching. -->
|
<!-- Keep SEQTA's structural attributes so existing themes recognize each row. -->
|
||||||
<!-- svelte-ignore a11y_no_noninteractive_element_to_interactive_role -->
|
<!-- svelte-ignore a11y_no_noninteractive_element_to_interactive_role -->
|
||||||
<li
|
<li
|
||||||
class="item bsplus-sidebar-item"
|
class="item bsplus-sidebar-item"
|
||||||
class:active={active}
|
class:bsplus-active={active}
|
||||||
class:hasChildren={item.hasChildren}
|
class:hasChildren={item.hasChildren}
|
||||||
class:edit-mode={editMode}
|
class:edit-mode={editMode}
|
||||||
class:hidden-item={editMode && !visible}
|
class:hidden-item={editMode && !visible}
|
||||||
class:compact={compact}
|
class:compact={compact}
|
||||||
class:draggable={editMode}
|
class:draggable={editMode}
|
||||||
style:--item-colour={item.itemColour || undefined}
|
style:--item-colour={item.itemColour || undefined}
|
||||||
|
data-colour={item.itemColour || undefined}
|
||||||
data-key={item.key}
|
data-key={item.key}
|
||||||
data-path={item.path ?? undefined}
|
data-path={item.path ?? undefined}
|
||||||
data-betterseqta={item.betterseqta ? "true" : undefined}
|
data-betterseqta={item.betterseqta ? "true" : undefined}
|
||||||
@@ -131,7 +132,7 @@
|
|||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bsplus-sidebar-item:hover:not(.active) {
|
.bsplus-sidebar-item:hover:not(.bsplus-active) {
|
||||||
background: rgba(0, 0, 0, 0.15);
|
background: rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,8 +141,8 @@
|
|||||||
box-shadow: 0 0 0 2px var(--theme-primary, #fff);
|
box-shadow: 0 0 0 2px var(--theme-primary, #fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
.bsplus-sidebar-item.active:not(.hasChildren),
|
.bsplus-sidebar-item.bsplus-active:not(.hasChildren),
|
||||||
.bsplus-sidebar-item.active:not(.hasChildren):hover {
|
.bsplus-sidebar-item.bsplus-active:not(.hasChildren):hover {
|
||||||
background: rgba(0, 0, 0, 0.35);
|
background: rgba(0, 0, 0, 0.35);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
@@ -250,18 +251,15 @@
|
|||||||
|
|
||||||
.colour-bar {
|
.colour-bar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
inset: 0;
|
||||||
left: 0;
|
border-radius: inherit;
|
||||||
bottom: 0;
|
box-shadow: inset 3px 0 var(--item-colour, transparent);
|
||||||
width: 3px;
|
transition: box-shadow 100ms ease;
|
||||||
border-radius: 8px 0 0 8px;
|
|
||||||
background: var(--item-colour, transparent);
|
|
||||||
transition: width 100ms ease;
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bsplus-sidebar-item:hover .colour-bar,
|
.bsplus-sidebar-item:hover .colour-bar,
|
||||||
.bsplus-sidebar-item.active .colour-bar {
|
.bsplus-sidebar-item.bsplus-active .colour-bar {
|
||||||
width: 6px;
|
box-shadow: inset 6px 0 var(--item-colour, transparent);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -37,12 +37,6 @@ function filterVisible(items: SidebarItem[]): SidebarItem[] {
|
|||||||
return items.filter((item) => menuItems[item.key]?.toggle !== false);
|
return items.filter((item) => menuItems[item.key]?.toggle !== false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureActive(el: Element | null | undefined) {
|
|
||||||
if (el instanceof HTMLElement && !el.classList.contains("active")) {
|
|
||||||
el.classList.add("active");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetSidebarScroll() {
|
function resetSidebarScroll() {
|
||||||
const root = document.getElementById("bsplus-sidebar-root");
|
const root = document.getElementById("bsplus-sidebar-root");
|
||||||
if (!(root instanceof HTMLElement)) return;
|
if (!(root instanceof HTMLElement)) return;
|
||||||
@@ -52,52 +46,6 @@ function resetSidebarScroll() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* SEQTA (and some themes) strip `.active` from `#menu li` after navigation.
|
|
||||||
* Theme decorations and drill `.sub` chrome depend on that class on our list.
|
|
||||||
*
|
|
||||||
* While drilling, never re-apply route-active on root leaves — themes like Beach
|
|
||||||
* paint palm/sand on `#menu > ul > li:not(.hasChildren).active`, and our `.sub`
|
|
||||||
* is transparent so those decorations show through over folder contents.
|
|
||||||
*/
|
|
||||||
export function restoreCustomMenuActive() {
|
|
||||||
const root = document.getElementById("bsplus-sidebar-root");
|
|
||||||
if (!root) return;
|
|
||||||
|
|
||||||
for (const li of root.querySelectorAll("li.hasChildren")) {
|
|
||||||
if (!(li instanceof HTMLElement)) continue;
|
|
||||||
if (!li.querySelector(":scope > .sub")) continue;
|
|
||||||
ensureActive(li);
|
|
||||||
}
|
|
||||||
|
|
||||||
const activeKey = sidebarState.activeKey;
|
|
||||||
const drilling = sidebarState.isDrilling;
|
|
||||||
|
|
||||||
if (drilling) {
|
|
||||||
if (activeKey) {
|
|
||||||
ensureActive(
|
|
||||||
root.querySelector(`.sub li.item[data-key="${CSS.escape(activeKey)}"]`),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
for (const li of root.querySelectorAll(
|
|
||||||
'.sub li.item[aria-current="page"]',
|
|
||||||
)) {
|
|
||||||
ensureActive(li);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeKey) {
|
|
||||||
ensureActive(
|
|
||||||
root.querySelector(`li.item[data-key="${CSS.escape(activeKey)}"]`),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const li of root.querySelectorAll('li.item[aria-current="page"]')) {
|
|
||||||
ensureActive(li);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Clear native drill state so it cannot steal pointer-events from the custom list. */
|
/** Clear native drill state so it cannot steal pointer-events from the custom list. */
|
||||||
export function clearNativeDrillActive(menu: HTMLElement) {
|
export function clearNativeDrillActive(menu: HTMLElement) {
|
||||||
getNativeMenuList(menu)
|
getNativeMenuList(menu)
|
||||||
@@ -251,7 +199,10 @@ class SidebarState {
|
|||||||
|
|
||||||
for (const frame of this.drillStack) {
|
for (const frame of this.drillStack) {
|
||||||
const folder = cursor.find((item) => item.key === frame.key);
|
const folder = cursor.find((item) => item.key === frame.key);
|
||||||
if (!folder?.hasChildren) break;
|
if (!folder?.hasChildren) {
|
||||||
|
next.push(...this.drillStack.slice(next.length));
|
||||||
|
break;
|
||||||
|
}
|
||||||
const children = filterVisible(folder.children);
|
const children = filterVisible(folder.children);
|
||||||
next.push({ key: folder.key, label: folder.label, items: children });
|
next.push({ key: folder.key, label: folder.label, items: children });
|
||||||
cursor = children;
|
cursor = children;
|
||||||
@@ -380,18 +331,7 @@ class SidebarState {
|
|||||||
|
|
||||||
const native = findNativeMenuEntry(menu, item);
|
const native = findNativeMenuEntry(menu, item);
|
||||||
if (native) {
|
if (native) {
|
||||||
// Clear native drill once only — repeating clearNativeDrillActive fights
|
|
||||||
// SEQTA (it re-adds .active) and used to freeze the tab via menu sync.
|
|
||||||
clearNativeDrillActive(menu);
|
|
||||||
native.click();
|
native.click();
|
||||||
clearNativeDrillActive(menu);
|
|
||||||
restoreCustomMenuActive();
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
clearNativeDrillActive(menu);
|
|
||||||
restoreCustomMenuActive();
|
|
||||||
});
|
|
||||||
// Later pass restores custom `.active` only (no native clear loop).
|
|
||||||
setTimeout(() => restoreCustomMenuActive(), 50);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ function sendPayload(payload: Record<string, unknown>): void {
|
|||||||
bridge.setAttribute("data-rev", String(Number(bridge.getAttribute("data-rev") || "0") + 1));
|
bridge.setAttribute("data-rev", String(Number(bridge.getAttribute("data-rev") || "0") + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function preserveCustomSidebarActive(css: string): string {
|
||||||
|
return css.replace(/\.active(?![\w-])/g, ":is(.active, .bsplus-active)");
|
||||||
|
}
|
||||||
|
|
||||||
export async function syncThemeToPage(input: ThemePageSyncInput): Promise<void> {
|
export async function syncThemeToPage(input: ThemePageSyncInput): Promise<void> {
|
||||||
if (input.clear) {
|
if (input.clear) {
|
||||||
sendPayload({ clear: true });
|
sendPayload({ clear: true });
|
||||||
@@ -63,8 +67,12 @@ export async function syncThemeToPage(input: ThemePageSyncInput): Promise<void>
|
|||||||
|
|
||||||
const payload: Record<string, unknown> = {};
|
const payload: Record<string, unknown> = {};
|
||||||
if (input.clearPreview) payload.clearPreview = true;
|
if (input.clearPreview) payload.clearPreview = true;
|
||||||
if (input.customCss !== undefined) payload.customCss = input.customCss;
|
if (input.customCss !== undefined) {
|
||||||
if (input.previewCss !== undefined) payload.previewCss = input.previewCss;
|
payload.customCss = preserveCustomSidebarActive(input.customCss);
|
||||||
|
}
|
||||||
|
if (input.previewCss !== undefined) {
|
||||||
|
payload.previewCss = preserveCustomSidebarActive(input.previewCss);
|
||||||
|
}
|
||||||
if (input.images !== undefined) {
|
if (input.images !== undefined) {
|
||||||
payload.images = await Promise.all(
|
payload.images = await Promise.all(
|
||||||
input.images.map(async (image) => ({
|
input.images.map(async (image) => ({
|
||||||
|
|||||||
Reference in New Issue
Block a user