web: refactor sidebar capabilities for categorical subsections
Move open/close logic into the ak-admin-sidebar itself. This commit removes the responsibility for opening/closing the sidebar from the interface parent code and places it inside the sidebar entirely. Since the Django invocation passes none of the properties ak-interface-admin is capable of receiving, this seems like a safe operation. The sidebar now assumes the responsibility for hooking up the window event listeners for open/close and resize. On connection to the DOM, and on resize, the sidebar checks to see if the viewport width meets the criteria for a behavioral change (slide-overlay vs slide-push), and on slide-push automatically opens the sidebar on the assumption that there's plenty of room. In order to support more dynamic styling going forward, I've substituted the 1280px with 80rem, which is the same, but allows for some better styling if someone with older eyes needs to "zoom in" on the whole thing with a larger font size. The hide/show code involves "reaching up" to touch the host's classList. There's a comment indicating that this is a slightly fragile thing to do, but in a well-known way.
This commit is contained in:
parent
e3b1ba63a6
commit
3e905cc956
|
@ -3,7 +3,6 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import {
|
import {
|
||||||
EVENT_API_DRAWER_TOGGLE,
|
EVENT_API_DRAWER_TOGGLE,
|
||||||
EVENT_NOTIFICATION_DRAWER_TOGGLE,
|
EVENT_NOTIFICATION_DRAWER_TOGGLE,
|
||||||
EVENT_SIDEBAR_TOGGLE,
|
|
||||||
} from "@goauthentik/common/constants";
|
} from "@goauthentik/common/constants";
|
||||||
import { configureSentry } from "@goauthentik/common/sentry";
|
import { configureSentry } from "@goauthentik/common/sentry";
|
||||||
import { me } from "@goauthentik/common/users";
|
import { me } from "@goauthentik/common/users";
|
||||||
|
@ -35,9 +34,6 @@ import "./AdminSidebar";
|
||||||
|
|
||||||
@customElement("ak-interface-admin")
|
@customElement("ak-interface-admin")
|
||||||
export class AdminInterface extends Interface {
|
export class AdminInterface extends Interface {
|
||||||
@property({ type: Boolean })
|
|
||||||
sidebarOpen = true;
|
|
||||||
|
|
||||||
@property({ type: Boolean })
|
@property({ type: Boolean })
|
||||||
notificationDrawerOpen = getURLParam("notificationDrawerOpen", false);
|
notificationDrawerOpen = getURLParam("notificationDrawerOpen", false);
|
||||||
|
|
||||||
|
@ -82,13 +78,6 @@ export class AdminInterface extends Interface {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.ws = new WebsocketClient();
|
this.ws = new WebsocketClient();
|
||||||
this.sidebarOpen = window.innerWidth >= 1280;
|
|
||||||
window.addEventListener("resize", () => {
|
|
||||||
this.sidebarOpen = window.innerWidth >= 1280;
|
|
||||||
});
|
|
||||||
window.addEventListener(EVENT_SIDEBAR_TOGGLE, () => {
|
|
||||||
this.sidebarOpen = !this.sidebarOpen;
|
|
||||||
});
|
|
||||||
window.addEventListener(EVENT_NOTIFICATION_DRAWER_TOGGLE, () => {
|
window.addEventListener(EVENT_NOTIFICATION_DRAWER_TOGGLE, () => {
|
||||||
this.notificationDrawerOpen = !this.notificationDrawerOpen;
|
this.notificationDrawerOpen = !this.notificationDrawerOpen;
|
||||||
updateURLParams({
|
updateURLParams({
|
||||||
|
@ -118,8 +107,6 @@ export class AdminInterface extends Interface {
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
const sidebarClasses = {
|
const sidebarClasses = {
|
||||||
"pf-m-expanded": this.sidebarOpen,
|
|
||||||
"pf-m-collapsed": !this.sidebarOpen,
|
|
||||||
"pf-m-light": this.activeTheme === UiThemeEnum.Light,
|
"pf-m-light": this.activeTheme === UiThemeEnum.Light,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -132,7 +119,6 @@ export class AdminInterface extends Interface {
|
||||||
return html` <ak-locale-context>
|
return html` <ak-locale-context>
|
||||||
<div class="pf-c-page">
|
<div class="pf-c-page">
|
||||||
<ak-admin-sidebar
|
<ak-admin-sidebar
|
||||||
?open=${this.sidebarOpen}
|
|
||||||
class="pf-c-page__sidebar ${classMap(sidebarClasses)}"
|
class="pf-c-page__sidebar ${classMap(sidebarClasses)}"
|
||||||
></ak-admin-sidebar>
|
></ak-admin-sidebar>
|
||||||
<div class="pf-c-page__drawer">
|
<div class="pf-c-page__drawer">
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import { VERSION } from "@goauthentik/common/constants";
|
import { EVENT_SIDEBAR_TOGGLE, VERSION } from "@goauthentik/common/constants";
|
||||||
import { me } from "@goauthentik/common/users";
|
import { me } from "@goauthentik/common/users";
|
||||||
import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts";
|
import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
import { ID_REGEX, SLUG_REGEX, UUID_REGEX } from "@goauthentik/elements/router/Route";
|
import { ID_REGEX, SLUG_REGEX, UUID_REGEX } from "@goauthentik/elements/router/Route";
|
||||||
|
import { getRootStyle } from "@goauthentik/elements/utils/getRootStyle";
|
||||||
import { spread } from "@open-wc/lit-helpers";
|
import { spread } from "@open-wc/lit-helpers";
|
||||||
|
|
||||||
import { consume } from "@lit-labs/context";
|
import { consume } from "@lit-labs/context";
|
||||||
|
@ -12,20 +13,12 @@ import { TemplateResult, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators.js";
|
import { customElement, property, state } from "lit/decorators.js";
|
||||||
import { map } from "lit/directives/map.js";
|
import { map } from "lit/directives/map.js";
|
||||||
|
|
||||||
import {
|
import { AdminApi, CapabilitiesEnum, CoreApi, UiThemeEnum, Version } from "@goauthentik/api";
|
||||||
AdminApi,
|
import type { Config, SessionUser, UserSelf } from "@goauthentik/api";
|
||||||
CapabilitiesEnum,
|
|
||||||
type Config,
|
|
||||||
CoreApi,
|
|
||||||
type SessionUser,
|
|
||||||
UiThemeEnum,
|
|
||||||
type UserSelf,
|
|
||||||
Version,
|
|
||||||
} from "@goauthentik/api";
|
|
||||||
|
|
||||||
@customElement("ak-admin-sidebar")
|
@customElement("ak-admin-sidebar")
|
||||||
export class AkAdminSidebar extends AKElement {
|
export class AkAdminSidebar extends AKElement {
|
||||||
@property({ type: Boolean })
|
@property({ type: Boolean, reflect: true })
|
||||||
open = true;
|
open = true;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
|
@ -45,6 +38,41 @@ export class AkAdminSidebar extends AKElement {
|
||||||
me().then((user: SessionUser) => {
|
me().then((user: SessionUser) => {
|
||||||
this.impersonation = user.original ? user.user.username : null;
|
this.impersonation = user.original ? user.user.username : null;
|
||||||
});
|
});
|
||||||
|
this.toggleOpen = this.toggleOpen.bind(this);
|
||||||
|
this.checkWidth = this.checkWidth.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This has to be a bound method so the event listener can be removed on disconnection as
|
||||||
|
// needed.
|
||||||
|
toggleOpen() {
|
||||||
|
this.open = !this.open;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkWidth() {
|
||||||
|
// This works just fine, but it assumes that the `--ak-sidebar--minimum-auto-width` is in
|
||||||
|
// REMs. If that changes, this code will have to be adjusted as well.
|
||||||
|
const minWidth =
|
||||||
|
parseFloat(getRootStyle("--ak-sidebar--minimum-auto-width")) *
|
||||||
|
parseFloat(getRootStyle("font-size"));
|
||||||
|
this.open = window.innerWidth >= minWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
window.addEventListener(EVENT_SIDEBAR_TOGGLE, this.toggleOpen);
|
||||||
|
window.addEventListener("resize", this.checkWidth);
|
||||||
|
// After connecting to the DOM, we can now perform this check to see if the sidebar should
|
||||||
|
// be open by default.
|
||||||
|
this.checkWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The symmetry (☟, ☝) here is critical in that you want to start adding these handlers after
|
||||||
|
// connection, and removing them before disconnection.
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
window.removeEventListener(EVENT_SIDEBAR_TOGGLE, this.toggleOpen);
|
||||||
|
window.removeEventListener("resize", this.checkWidth);
|
||||||
|
super.disconnectedCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -60,6 +88,16 @@ export class AkAdminSidebar extends AKElement {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updated() {
|
||||||
|
// This is permissible as`:host.classList` is not one of the properties Lit uses as a
|
||||||
|
// scheduling trigger. This sort of shenanigans can trigger an loop, in that it will trigger
|
||||||
|
// a browser reflow, which may trigger some other styling the application is monitoring,
|
||||||
|
// triggering a re-render which triggers a browser reflow, ad infinitum. But we've been
|
||||||
|
// living with that since jQuery, and it's both well-known and fortunately rare.
|
||||||
|
this.classList.remove("pf-m-expanded", "pf-m-collapsed");
|
||||||
|
this.classList.add(this.open ? "pf-m-expanded" : "pf-m-collapsed");
|
||||||
|
}
|
||||||
|
|
||||||
renderSidebarItems(): TemplateResult {
|
renderSidebarItems(): TemplateResult {
|
||||||
// The second attribute type is of string[] to help with the 'activeWhen' control, which was
|
// The second attribute type is of string[] to help with the 'activeWhen' control, which was
|
||||||
// commonplace and singular enough to merit its own handler.
|
// commonplace and singular enough to merit its own handler.
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
|
|
||||||
/* PatternFly likes to override global variables for some reason */
|
/* PatternFly likes to override global variables for some reason */
|
||||||
--ak-global--Color--100: var(--pf-global--Color--100);
|
--ak-global--Color--100: var(--pf-global--Color--100);
|
||||||
|
|
||||||
|
/* Minimum width after which the sidebar becomes automatic */
|
||||||
|
--ak-sidebar--minimum-auto-width: 80rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
|
|
Reference in a new issue