fix:collapsibleUI (#1243)

* refactor:collapsibleUI

Add surfaceClass API / rewrite collapsible

* fix: satisfy collapsibleUI stylelint

* ok papi

* Update CollapsibleUI

* Updates

* bleh

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* fix userarea gap issue

* [autofix.ci] apply automated fixes

* userarea pill matches theme + betterfolders support

* removed :has, Channellist grid issue resolved, userarea isse resolved

* [autofix.ci] apply automated fixes

---------

Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Benjii 2026-07-11 00:55:43 +01:00 • committed by GitHub
parent 1dcb8d9d0f
commit 93371a1863
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 61 additions and 13 deletions

View file

@ -20,8 +20,8 @@ import managedStyle from "./style.css?managed";
const cl = classNameFactory("vc-collapsible-ui-");
const panelDependentSurfaces: Record<PanelId, SurfaceId[]> = {
guildBar: ["guildBar", "userArea"],
channelList: ["channelList", "base", "sidebar", "userArea"],
guildBar: ["guildBar", "sidebar", "userArea"],
channelList: ["channelList", "base", "sidebar"],
membersList: ["membersList"],
chatButtons: [],
titleBar: ["titleBar"],
@ -37,6 +37,7 @@ const DETACHED_USER_AREA_DEFAULT_OFFSET_Y = 88;
let providerUnsubs: Array<() => void> = [];
let channelListExpandedByInteraction = false;
let guildBarExpandedByInteraction = false;
let headerBarExpandedByInteraction = false;
let headerBarPointerTrackerEnabled = false;
let userAreaDragState: { offsetX: number; offsetY: number; width: number; height: number; } | undefined;
@ -84,7 +85,17 @@ function setChannelListExpandedByInteraction(expanded: boolean) {
notifySurfaceClassesChanged("sidebar");
}
function setGuildBarExpandedByInteraction(expanded: boolean) {
if (guildBarExpandedByInteraction === expanded) return;
guildBarExpandedByInteraction = expanded;
notifySurfaceClassesChanged("guildBar");
}
function syncPanelCollapsedState(panelId: PanelId, collapsed: boolean) {
if (panelId === "guildBar") {
guildBarExpandedByInteraction = false;
}
if (panelId === "channelList") {
channelListExpandedByInteraction = false;
}
@ -187,7 +198,17 @@ function eventWithin(element: HTMLElement | null, event: ReactFocusEvent<HTMLEle
return false;
}
// The BetterFolders sidebar mounts and unmounts with folder state, so match
// it by class instead of holding an element reference.
function eventWithinBetterFolders(event: ReactFocusEvent<HTMLElement> | ReactMouseEvent<HTMLElement>) {
return event.target instanceof Element && !!event.target.closest(".vc-betterFolders-sidebar");
}
function handleSidebarEnter(event: ReactFocusEvent<HTMLElement> | ReactMouseEvent<HTMLElement>) {
if (isPanelCollapsed("guildBar")) {
setGuildBarExpandedByInteraction(eventWithinBetterFolders(event));
}
if (eventWithin(userAreaElement, event)) {
setChannelListExpandedByInteraction(false);
return;
@ -199,6 +220,10 @@ function handleSidebarEnter(event: ReactFocusEvent<HTMLElement> | ReactMouseEven
}
function handleSidebarLeave(event: ReactFocusEvent<HTMLElement> | ReactMouseEvent<HTMLElement>) {
if (!(event.relatedTarget instanceof Element && event.relatedTarget.closest(".vc-betterFolders-sidebar"))) {
setGuildBarExpandedByInteraction(false);
}
if (nodeWithin(userAreaElement, event.relatedTarget)) {
setChannelListExpandedByInteraction(false);
return;
@ -446,7 +471,14 @@ export default definePlugin({
} as SurfaceProvidedProps);
providerUnsubs = [
addSurfacePropsProvider("guildBar", () => panelAttr(panelRegistry.guildBar.classId, isPanelCollapsed("guildBar"))),
addSurfacePropsProvider("guildBar", () => {
const collapsed = isPanelCollapsed("guildBar");
const attrs: SurfaceProvidedProps = panelAttr(panelRegistry.guildBar.classId, collapsed);
if (collapsed && guildBarExpandedByInteraction) {
attrs["data-vc-collapsible-ui-guild-bar-interaction-expanded"] = "";
}
return attrs;
}),
addSurfacePropsProvider("channelList", () => {
const attrs: SurfaceProvidedProps = panelAttr(panelRegistry.channelList.classId, isPanelCollapsed("channelList"));
attrs.ref = setChannelListElement;
@ -471,7 +503,6 @@ export default definePlugin({
}),
addSurfacePropsProvider("userArea", () => {
const uaCollapsed = isPanelCollapsed("userArea");
const clCollapsed = isPanelCollapsed("channelList");
const gbCollapsed = isPanelCollapsed("guildBar");
const userAreaDetached = shouldDetachUserArea();
const attrs: SurfaceProvidedProps = panelAttr(panelRegistry.userArea.classId, uaCollapsed);
@ -486,9 +517,6 @@ export default definePlugin({
};
attrs.onMouseDownCapture = startDetachedUserAreaDrag;
}
if (!userAreaDetached && clCollapsed) {
attrs["data-vc-collapsible-ui-user-area-channel-list-collapsed"] = "";
}
if (!userAreaDetached && gbCollapsed) {
attrs["data-vc-collapsible-ui-user-area-guild-bar-collapsed"] = "";
}
@ -512,6 +540,7 @@ export default definePlugin({
"data-vc-collapsible-ui-sidebar": "",
[`data-vc-collapsible-ui-sidebar-channel-list-${collapsed ? "collapsed" : "expanded"}`]: "",
...(collapsed && channelListExpandedByInteraction ? { "data-vc-collapsible-ui-sidebar-channel-list-expanded": "" } : {}),
...(isPanelCollapsed("guildBar") ? { "data-vc-collapsible-ui-sidebar-guild-bar-collapsed": "" } : {}),
...(userAreaDetached ? { "data-vc-collapsible-ui-sidebar-user-area-detached": "" } : {}),
onFocusCapture: handleSidebarEnter,
onBlurCapture: handleSidebarLeave,
@ -536,6 +565,7 @@ export default definePlugin({
channelListElement = null;
userAreaElement = null;
channelListExpandedByInteraction = false;
guildBarExpandedByInteraction = false;
headerBarExpandedByInteraction = false;
surfaceCssPxCache.clear();
},

View file

@ -23,10 +23,22 @@
[data-vc-collapsible-ui-base-channel-list-collapsed] {
/* stylelint-disable-next-line value-keyword-case */
grid-template-columns: [start] min-content [guildsEnd] min-content [sidebarEnd] var(--vc-cui-peek-size) [channelsEnd] 1fr [end] !important;
grid-template-columns: [start] min-content [guildsEnd] var(--vc-cui-peek-size) [channelsEnd] 1fr [end] !important;
}
[data-vc-collapsible-ui-base-channel-list-collapsed][data-vc-collapsible-ui-base-channel-list-interaction-expanded] {
/* stylelint-disable-next-line value-keyword-case */
grid-template-columns: [start] min-content [guildsEnd] var(--custom-guild-sidebar-width, 312px) [channelsEnd] 1fr [end] !important;
}
/* BetterFolders redefines the base grid with an extra folder column, so the
collapsed templates must keep its [sidebarEnd] track to stay in sync. */
[data-vc-collapsible-ui-base-channel-list-collapsed].vc-betterFolders-sidebar-grid {
/* stylelint-disable-next-line value-keyword-case */
grid-template-columns: [start] min-content [guildsEnd] min-content [sidebarEnd] var(--vc-cui-peek-size) [channelsEnd] 1fr [end] !important;
}
[data-vc-collapsible-ui-base-channel-list-collapsed][data-vc-collapsible-ui-base-channel-list-interaction-expanded].vc-betterFolders-sidebar-grid {
/* stylelint-disable-next-line value-keyword-case */
grid-template-columns: [start] min-content [guildsEnd] min-content [sidebarEnd] var(--custom-guild-sidebar-width, 312px) [channelsEnd] 1fr [end] !important;
}
@ -67,8 +79,8 @@
transition-delay: var(--vc-cui-fade-delay);
}
[data-vc-collapsible-ui-guild-bar-collapsed]:not(.vc-betterFolders-sidebar *):is(:hover, :focus-within),
[data-vc-collapsible-ui-base]:has(.vc-betterFolders-sidebar:is(:hover, :focus-within)) [data-vc-collapsible-ui-guild-bar-collapsed]:not(.vc-betterFolders-sidebar *) {
[data-vc-collapsible-ui-guild-bar-collapsed]:not(.vc-betterFolders-sidebar *):hover,
[data-vc-collapsible-ui-guild-bar-collapsed][data-vc-collapsible-ui-guild-bar-interaction-expanded]:not(.vc-betterFolders-sidebar *) {
width: var(--vc-cui-guild-bar-expanded-width) !important;
min-width: var(--vc-cui-guild-bar-expanded-width) !important;
max-width: var(--vc-cui-guild-bar-expanded-width) !important;
@ -80,7 +92,7 @@
/* BetterFolders renders a duplicate guilds nav inside its folder sidebar.
The collapse rules above exempt that duplicate; collapse the sidebar
wrapper in sync with the real guild bar instead. */
[data-vc-collapsible-ui-base]:has([data-vc-collapsible-ui-guild-bar-collapsed]) .vc-betterFolders-sidebar {
[data-vc-collapsible-ui-sidebar-guild-bar-collapsed] .vc-betterFolders-sidebar {
width: 0 !important;
min-width: 0 !important;
opacity: 0.01;
@ -89,8 +101,8 @@
transition-delay: var(--vc-cui-fade-delay);
}
[data-vc-collapsible-ui-base]:has([data-vc-collapsible-ui-guild-bar-collapsed]:is(:hover, :focus-within)) .vc-betterFolders-sidebar,
[data-vc-collapsible-ui-base]:has([data-vc-collapsible-ui-guild-bar-collapsed]) .vc-betterFolders-sidebar:is(:hover, :focus-within) {
[data-vc-collapsible-ui-guild-bar-collapsed]:hover ~ .vc-betterFolders-sidebar,
[data-vc-collapsible-ui-sidebar-guild-bar-collapsed] .vc-betterFolders-sidebar:hover {
width: auto !important;
opacity: 1;
transition-delay: 0ms;
@ -279,6 +291,12 @@
max-width: var(--vc-cui-peek-size) !important;
}
[data-vc-collapsible-ui-sidebar-channel-list-collapsed]:not([data-vc-collapsible-ui-sidebar-user-area-detached]) [data-vc-collapsible-ui-user-area-collapsed]:is(:hover, :focus-within) {
width: var(--custom-guild-sidebar-width, 312px) !important;
min-width: var(--vc-cui-channel-list-min-width) !important;
max-width: var(--vc-cui-channel-list-max-width) !important;
}
[data-vc-collapsible-ui-sidebar-channel-list-expanded]:not([data-vc-collapsible-ui-sidebar-user-area-detached]) [data-vc-collapsible-ui-user-area] {
/* Anchor both edges instead of forcing a width: the guild bar column is
min-content and theme-dependent, so a fixed width leaves a right-side