Compare commits
	
		
			1 Commits
		
	
	
		
			website/in
			...
			safari-cra
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 76c3c7d968 | 
@ -4,6 +4,7 @@ import { ROUTES } from "@goauthentik/admin/Routes";
 | 
				
			|||||||
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";
 | 
				
			||||||
@ -11,6 +12,8 @@ import { WebsocketClient } from "@goauthentik/common/ws";
 | 
				
			|||||||
import { AuthenticatedInterface } from "@goauthentik/elements/Interface";
 | 
					import { AuthenticatedInterface } from "@goauthentik/elements/Interface";
 | 
				
			||||||
import "@goauthentik/elements/ak-locale-context";
 | 
					import "@goauthentik/elements/ak-locale-context";
 | 
				
			||||||
import "@goauthentik/elements/banner/EnterpriseStatusBanner";
 | 
					import "@goauthentik/elements/banner/EnterpriseStatusBanner";
 | 
				
			||||||
 | 
					import "@goauthentik/elements/banner/EnterpriseStatusBanner";
 | 
				
			||||||
 | 
					import "@goauthentik/elements/banner/VersionBanner";
 | 
				
			||||||
import "@goauthentik/elements/banner/VersionBanner";
 | 
					import "@goauthentik/elements/banner/VersionBanner";
 | 
				
			||||||
import "@goauthentik/elements/messages/MessageContainer";
 | 
					import "@goauthentik/elements/messages/MessageContainer";
 | 
				
			||||||
import "@goauthentik/elements/messages/MessageContainer";
 | 
					import "@goauthentik/elements/messages/MessageContainer";
 | 
				
			||||||
@ -40,6 +43,8 @@ if (process.env.NODE_ENV === "development") {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@customElement("ak-interface-admin")
 | 
					@customElement("ak-interface-admin")
 | 
				
			||||||
export class AdminInterface extends AuthenticatedInterface {
 | 
					export class AdminInterface extends AuthenticatedInterface {
 | 
				
			||||||
 | 
					    //#region Properties
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property({ type: Boolean })
 | 
					    @property({ type: Boolean })
 | 
				
			||||||
    notificationDrawerOpen = getURLParam("notificationDrawerOpen", false);
 | 
					    notificationDrawerOpen = getURLParam("notificationDrawerOpen", false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -54,6 +59,17 @@ export class AdminInterface extends AuthenticatedInterface {
 | 
				
			|||||||
    @query("ak-about-modal")
 | 
					    @query("ak-about-modal")
 | 
				
			||||||
    aboutModal?: AboutModal;
 | 
					    aboutModal?: AboutModal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @state()
 | 
				
			||||||
 | 
					    sidebarVisible = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #toggleSidebar = () => {
 | 
				
			||||||
 | 
					        this.sidebarVisible = !this.sidebarVisible;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //#region Styles
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static get styles(): CSSResult[] {
 | 
					    static get styles(): CSSResult[] {
 | 
				
			||||||
        return [
 | 
					        return [
 | 
				
			||||||
            PFBase,
 | 
					            PFBase,
 | 
				
			||||||
@ -67,23 +83,30 @@ export class AdminInterface extends AuthenticatedInterface {
 | 
				
			|||||||
                    z-index: auto !important;
 | 
					                    z-index: auto !important;
 | 
				
			||||||
                    background-color: transparent;
 | 
					                    background-color: transparent;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                .display-none {
 | 
					                .display-none {
 | 
				
			||||||
                    display: none;
 | 
					                    display: none;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                .pf-c-page {
 | 
					                .pf-c-page {
 | 
				
			||||||
                    background-color: var(--pf-c-page--BackgroundColor) !important;
 | 
					                    background-color: var(--pf-c-page--BackgroundColor) !important;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                /* Global page background colour */
 | 
					
 | 
				
			||||||
                :host([theme="dark"]) .pf-c-page {
 | 
					                :host([theme="dark"]) {
 | 
				
			||||||
                    --pf-c-page--BackgroundColor: var(--ak-dark-background);
 | 
					                    /* Global page background colour */
 | 
				
			||||||
 | 
					                    .pf-c-page {
 | 
				
			||||||
 | 
					                        --pf-c-page--BackgroundColor: var(--ak-dark-background);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                ak-enterprise-status,
 | 
					
 | 
				
			||||||
                ak-version-banner {
 | 
					                ak-page-navbar {
 | 
				
			||||||
                    grid-area: header;
 | 
					                    grid-area: header;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                ak-admin-sidebar {
 | 
					                ak-admin-sidebar {
 | 
				
			||||||
                    grid-area: nav;
 | 
					                    grid-area: nav;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                .pf-c-drawer__panel {
 | 
					                .pf-c-drawer__panel {
 | 
				
			||||||
                    z-index: var(--pf-global--ZIndex--xl);
 | 
					                    z-index: var(--pf-global--ZIndex--xl);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -91,6 +114,10 @@ export class AdminInterface extends AuthenticatedInterface {
 | 
				
			|||||||
        ];
 | 
					        ];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //#region Lifecycle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        super();
 | 
					        super();
 | 
				
			||||||
        this.ws = new WebsocketClient();
 | 
					        this.ws = new WebsocketClient();
 | 
				
			||||||
@ -123,12 +150,26 @@ export class AdminInterface extends AuthenticatedInterface {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async connectedCallback(): Promise<void> {
 | 
				
			||||||
 | 
					        super.connectedCallback();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        window.addEventListener(EVENT_SIDEBAR_TOGGLE, this.#toggleSidebar);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    disconnectedCallback(): void {
 | 
				
			||||||
 | 
					        super.disconnectedCallback();
 | 
				
			||||||
 | 
					        window.removeEventListener(EVENT_SIDEBAR_TOGGLE, this.#toggleSidebar);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render(): TemplateResult {
 | 
					    render(): TemplateResult {
 | 
				
			||||||
        const sidebarClasses = {
 | 
					        const sidebarClasses = {
 | 
				
			||||||
            "pf-m-light": this.activeTheme === UiThemeEnum.Light,
 | 
					            "pf-m-light": this.activeTheme === UiThemeEnum.Light,
 | 
				
			||||||
 | 
					            "pf-m-expanded": !this.sidebarVisible,
 | 
				
			||||||
 | 
					            "pf-m-collapsed": this.sidebarVisible,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const drawerOpen = this.notificationDrawerOpen || this.apiDrawerOpen;
 | 
					        const drawerOpen = this.notificationDrawerOpen || this.apiDrawerOpen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const drawerClasses = {
 | 
					        const drawerClasses = {
 | 
				
			||||||
            "pf-m-expanded": drawerOpen,
 | 
					            "pf-m-expanded": drawerOpen,
 | 
				
			||||||
            "pf-m-collapsed": !drawerOpen,
 | 
					            "pf-m-collapsed": !drawerOpen,
 | 
				
			||||||
@ -136,11 +177,16 @@ export class AdminInterface extends AuthenticatedInterface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return html` <ak-locale-context>
 | 
					        return html` <ak-locale-context>
 | 
				
			||||||
            <div class="pf-c-page">
 | 
					            <div class="pf-c-page">
 | 
				
			||||||
                <ak-enterprise-status interface="admin"></ak-enterprise-status>
 | 
					                <ak-page-navbar>
 | 
				
			||||||
                <ak-version-banner></ak-version-banner>
 | 
					                    <ak-version-banner></ak-version-banner>
 | 
				
			||||||
 | 
					                    <ak-enterprise-status interface="admin"></ak-enterprise-status>
 | 
				
			||||||
 | 
					                </ak-page-navbar>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <ak-admin-sidebar
 | 
					                <ak-admin-sidebar
 | 
				
			||||||
                    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">
 | 
				
			||||||
                    <div class="pf-c-drawer ${classMap(drawerClasses)}">
 | 
					                    <div class="pf-c-drawer ${classMap(drawerClasses)}">
 | 
				
			||||||
                        <div class="pf-c-drawer__main">
 | 
					                        <div class="pf-c-drawer__main">
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,3 @@
 | 
				
			|||||||
import { EVENT_SIDEBAR_TOGGLE } from "@goauthentik/common/constants";
 | 
					 | 
				
			||||||
import { me } from "@goauthentik/common/users";
 | 
					import { me } from "@goauthentik/common/users";
 | 
				
			||||||
import { AKElement } from "@goauthentik/elements/Base";
 | 
					import { AKElement } from "@goauthentik/elements/Base";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
@ -31,16 +30,9 @@ export class AkAdminSidebar extends WithCapabilitiesConfig(WithVersion(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.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() {
 | 
					    checkWidth() {
 | 
				
			||||||
        // This works just fine, but it assumes that the `--ak-sidebar--minimum-auto-width` is in
 | 
					        // 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.
 | 
					        // REMs. If that changes, this code will have to be adjusted as well.
 | 
				
			||||||
@ -52,7 +44,7 @@ export class AkAdminSidebar extends WithCapabilitiesConfig(WithVersion(AKElement
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    connectedCallback() {
 | 
					    connectedCallback() {
 | 
				
			||||||
        super.connectedCallback();
 | 
					        super.connectedCallback();
 | 
				
			||||||
        window.addEventListener(EVENT_SIDEBAR_TOGGLE, this.toggleOpen);
 | 
					
 | 
				
			||||||
        window.addEventListener("resize", this.checkWidth);
 | 
					        window.addEventListener("resize", this.checkWidth);
 | 
				
			||||||
        // After connecting to the DOM, we can now perform this check to see if the sidebar should
 | 
					        // After connecting to the DOM, we can now perform this check to see if the sidebar should
 | 
				
			||||||
        // be open by default.
 | 
					        // be open by default.
 | 
				
			||||||
@ -63,7 +55,6 @@ export class AkAdminSidebar extends WithCapabilitiesConfig(WithVersion(AKElement
 | 
				
			|||||||
    // connection, and removing them before disconnection.
 | 
					    // connection, and removing them before disconnection.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    disconnectedCallback() {
 | 
					    disconnectedCallback() {
 | 
				
			||||||
        window.removeEventListener(EVENT_SIDEBAR_TOGGLE, this.toggleOpen);
 | 
					 | 
				
			||||||
        window.removeEventListener("resize", this.checkWidth);
 | 
					        window.removeEventListener("resize", this.checkWidth);
 | 
				
			||||||
        super.disconnectedCallback();
 | 
					        super.disconnectedCallback();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -71,8 +62,9 @@ export class AkAdminSidebar extends WithCapabilitiesConfig(WithVersion(AKElement
 | 
				
			|||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        return html`
 | 
					        return html`
 | 
				
			||||||
            <ak-sidebar
 | 
					            <ak-sidebar
 | 
				
			||||||
                class="pf-c-page__sidebar ${this.open ? "pf-m-expanded" : "pf-m-collapsed"} ${this
 | 
					                class="pf-c-page__sidebar
 | 
				
			||||||
                    .activeTheme === UiThemeEnum.Light
 | 
					                ${this.open ? "pf-m-expanded" : "pf-m-collapsed"} ${this.activeTheme ===
 | 
				
			||||||
 | 
					                UiThemeEnum.Light
 | 
				
			||||||
                    ? "pf-m-light"
 | 
					                    ? "pf-m-light"
 | 
				
			||||||
                    : ""}"
 | 
					                    : ""}"
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
@ -81,19 +73,6 @@ export class AkAdminSidebar extends WithCapabilitiesConfig(WithVersion(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.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // eslint-disable-next-line wc/no-self-class
 | 
					 | 
				
			||||||
        this.classList.remove("pf-m-expanded", "pf-m-collapsed");
 | 
					 | 
				
			||||||
        // eslint-disable-next-line wc/no-self-class
 | 
					 | 
				
			||||||
        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.
 | 
				
			||||||
 | 
				
			|||||||
@ -94,10 +94,13 @@ export class AdminOverviewPage extends AdminOverviewBase {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render(): TemplateResult {
 | 
					    render(): TemplateResult {
 | 
				
			||||||
        const name = this.user?.user.name ?? this.user?.user.username;
 | 
					        const username = this.user?.user.name || this.user?.user.username;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return html`<ak-page-header description=${msg("General system status")} ?hasIcon=${false}>
 | 
					        return html` <ak-page-header
 | 
				
			||||||
                <span slot="header"> ${msg(str`Welcome, ${name || ""}.`)} </span>
 | 
					                header=${msg(str`Welcome, ${username || ""}.`)}
 | 
				
			||||||
 | 
					                description=${msg("General system status")}
 | 
				
			||||||
 | 
					                ?hasIcon=${false}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
            </ak-page-header>
 | 
					            </ak-page-header>
 | 
				
			||||||
            <section class="pf-c-page__main-section">
 | 
					            <section class="pf-c-page__main-section">
 | 
				
			||||||
                <div class="pf-l-grid pf-m-gutter">
 | 
					                <div class="pf-l-grid pf-m-gutter">
 | 
				
			||||||
 | 
				
			|||||||
@ -83,13 +83,10 @@ export class AdminSettingsPage extends AKElement {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        if (!this.settings) {
 | 
					        if (!this.settings) return nothing;
 | 
				
			||||||
            return nothing;
 | 
					
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return html`
 | 
					        return html`
 | 
				
			||||||
            <ak-page-header icon="fa fa-cog" header="" description="">
 | 
					            <ak-page-header icon="fa fa-cog" header="${msg("System settings")}"> </ak-page-header>
 | 
				
			||||||
                <span slot="header"> ${msg("System settings")} </span>
 | 
					 | 
				
			||||||
            </ak-page-header>
 | 
					 | 
				
			||||||
            <section class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter">
 | 
					            <section class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter">
 | 
				
			||||||
                <div class="pf-c-card">
 | 
					                <div class="pf-c-card">
 | 
				
			||||||
                    <div class="pf-c-card__body">
 | 
					                    <div class="pf-c-card__body">
 | 
				
			||||||
 | 
				
			|||||||
@ -17,6 +17,13 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /* Minimum width after which the sidebar becomes automatic */
 | 
					    /* Minimum width after which the sidebar becomes automatic */
 | 
				
			||||||
    --ak-sidebar--minimum-auto-width: 80rem;
 | 
					    --ak-sidebar--minimum-auto-width: 80rem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The height of the navbar and branded sidebar.
 | 
				
			||||||
 | 
					     * @todo This shouldn't be necessary. The sidebar can instead use a grid layout
 | 
				
			||||||
 | 
					     * ensuring they share the same height.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    --ak-navbar--height: 7rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@supports selector(::-webkit-scrollbar) {
 | 
					@supports selector(::-webkit-scrollbar) {
 | 
				
			||||||
 | 
				
			|||||||
@ -67,6 +67,12 @@ export class NavigationButtons extends AKElement {
 | 
				
			|||||||
                :host([theme="light"]) .pf-c-page__header-tools-group .pf-c-button {
 | 
					                :host([theme="light"]) .pf-c-page__header-tools-group .pf-c-button {
 | 
				
			||||||
                    color: var(--ak-global--Color--100) !important;
 | 
					                    color: var(--ak-global--Color--100) !important;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                @media (max-width: 768px) {
 | 
				
			||||||
 | 
					                    .pf-c-avatar {
 | 
				
			||||||
 | 
					                        display: none;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            `,
 | 
					            `,
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -156,9 +162,7 @@ export class NavigationButtons extends AKElement {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    renderImpersonation() {
 | 
					    renderImpersonation() {
 | 
				
			||||||
        if (!this.me?.original) {
 | 
					        if (!this.me?.original) return nothing;
 | 
				
			||||||
            return nothing;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const onClick = async () => {
 | 
					        const onClick = async () => {
 | 
				
			||||||
            await new CoreApi(DEFAULT_CONFIG).coreUsersImpersonateEndRetrieve();
 | 
					            await new CoreApi(DEFAULT_CONFIG).coreUsersImpersonateEndRetrieve();
 | 
				
			||||||
@ -175,6 +179,14 @@ export class NavigationButtons extends AKElement {
 | 
				
			|||||||
            </div>`;
 | 
					            </div>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    renderAvatar() {
 | 
				
			||||||
 | 
					        return html`<img
 | 
				
			||||||
 | 
					            class="pf-c-avatar"
 | 
				
			||||||
 | 
					            src=${ifDefined(this.me?.user.avatar)}
 | 
				
			||||||
 | 
					            alt="${msg("Avatar image")}"
 | 
				
			||||||
 | 
					        />`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    get userDisplayName() {
 | 
					    get userDisplayName() {
 | 
				
			||||||
        return match<UserDisplay | undefined, string | undefined>(this.uiConfig?.navbar.userDisplay)
 | 
					        return match<UserDisplay | undefined, string | undefined>(this.uiConfig?.navbar.userDisplay)
 | 
				
			||||||
            .with(UserDisplay.username, () => this.me?.user.username)
 | 
					            .with(UserDisplay.username, () => this.me?.user.username)
 | 
				
			||||||
@ -212,11 +224,7 @@ export class NavigationButtons extends AKElement {
 | 
				
			|||||||
                      </div>
 | 
					                      </div>
 | 
				
			||||||
                  </div>`
 | 
					                  </div>`
 | 
				
			||||||
                : nothing}
 | 
					                : nothing}
 | 
				
			||||||
            <img
 | 
					            ${this.renderAvatar()}
 | 
				
			||||||
                class="pf-c-avatar"
 | 
					 | 
				
			||||||
                src=${ifDefined(this.me?.user.avatar)}
 | 
					 | 
				
			||||||
                alt="${msg("Avatar image")}"
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
        </div>`;
 | 
					        </div>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -10,15 +10,18 @@ import { me } from "@goauthentik/common/users";
 | 
				
			|||||||
import "@goauthentik/components/ak-nav-buttons";
 | 
					import "@goauthentik/components/ak-nav-buttons";
 | 
				
			||||||
import { AKElement } from "@goauthentik/elements/Base";
 | 
					import { AKElement } from "@goauthentik/elements/Base";
 | 
				
			||||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
 | 
					import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
 | 
				
			||||||
 | 
					import { DefaultBrand } from "@goauthentik/elements/sidebar/SidebarBrand";
 | 
				
			||||||
 | 
					import { themeImage } from "@goauthentik/elements/utils/images";
 | 
				
			||||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
 | 
					import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { msg } from "@lit/localize";
 | 
					import { msg } from "@lit/localize";
 | 
				
			||||||
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
 | 
					import { CSSResult, LitElement, TemplateResult, css, html, nothing } from "lit";
 | 
				
			||||||
import { customElement, property, state } from "lit/decorators.js";
 | 
					import { customElement, property, state } from "lit/decorators.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
 | 
					import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
 | 
				
			||||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
 | 
					import PFButton from "@patternfly/patternfly/components/Button/button.css";
 | 
				
			||||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
 | 
					import PFContent from "@patternfly/patternfly/components/Content/content.css";
 | 
				
			||||||
 | 
					import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";
 | 
				
			||||||
import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";
 | 
					import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";
 | 
				
			||||||
import PFNotificationBadge from "@patternfly/patternfly/components/NotificationBadge/notification-badge.css";
 | 
					import PFNotificationBadge from "@patternfly/patternfly/components/NotificationBadge/notification-badge.css";
 | 
				
			||||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
 | 
					import PFPage from "@patternfly/patternfly/components/Page/page.css";
 | 
				
			||||||
@ -26,34 +29,52 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import { SessionUser } from "@goauthentik/api";
 | 
					import { SessionUser } from "@goauthentik/api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@customElement("ak-page-header")
 | 
					//#region Page Navbar
 | 
				
			||||||
export class PageHeader extends WithBrandConfig(AKElement) {
 | 
					 | 
				
			||||||
    @property()
 | 
					 | 
				
			||||||
    icon?: string;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property({ type: Boolean })
 | 
					export interface PageNavbarDetails {
 | 
				
			||||||
    iconImage = false;
 | 
					    header?: string;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property()
 | 
					 | 
				
			||||||
    header = "";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property()
 | 
					 | 
				
			||||||
    description?: string;
 | 
					    description?: string;
 | 
				
			||||||
 | 
					    icon?: string;
 | 
				
			||||||
 | 
					    iconImage?: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property({ type: Boolean })
 | 
					/**
 | 
				
			||||||
    hasIcon = true;
 | 
					 * A global navbar component at the top of the page.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Internally, this component listens for the `ak-page-header` event, which is
 | 
				
			||||||
 | 
					 * dispatched by the `ak-page-header` component.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@customElement("ak-page-navbar")
 | 
				
			||||||
 | 
					export class AKPageNavbar extends WithBrandConfig(AKElement) implements PageNavbarDetails {
 | 
				
			||||||
 | 
					    //#region Static Properties
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @state()
 | 
					    private static elementRef: AKPageNavbar | null = null;
 | 
				
			||||||
    me?: SessionUser;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @state()
 | 
					    static readonly setNavbarDetails = (detail: Partial<PageNavbarDetails>): void => {
 | 
				
			||||||
    uiConfig!: UIConfig;
 | 
					        const { elementRef } = AKPageNavbar;
 | 
				
			||||||
 | 
					        if (!elementRef) {
 | 
				
			||||||
 | 
					            console.debug(
 | 
				
			||||||
 | 
					                `ak-page-header: Could not find ak-page-navbar, skipping event dispatch.`,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const { header, description, icon, iconImage } = detail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        elementRef.header = header;
 | 
				
			||||||
 | 
					        elementRef.description = description;
 | 
				
			||||||
 | 
					        elementRef.icon = icon;
 | 
				
			||||||
 | 
					        elementRef.iconImage = iconImage || false;
 | 
				
			||||||
 | 
					        elementRef.hasIcon = !!icon;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static get styles(): CSSResult[] {
 | 
					    static get styles(): CSSResult[] {
 | 
				
			||||||
        return [
 | 
					        return [
 | 
				
			||||||
            PFBase,
 | 
					            PFBase,
 | 
				
			||||||
            PFButton,
 | 
					            PFButton,
 | 
				
			||||||
            PFPage,
 | 
					            PFPage,
 | 
				
			||||||
 | 
					            PFDrawer,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            PFNotificationBadge,
 | 
					            PFNotificationBadge,
 | 
				
			||||||
            PFContent,
 | 
					            PFContent,
 | 
				
			||||||
            PFAvatar,
 | 
					            PFAvatar,
 | 
				
			||||||
@ -63,55 +84,212 @@ export class PageHeader extends WithBrandConfig(AKElement) {
 | 
				
			|||||||
                    position: sticky;
 | 
					                    position: sticky;
 | 
				
			||||||
                    top: 0;
 | 
					                    top: 0;
 | 
				
			||||||
                    z-index: var(--pf-global--ZIndex--lg);
 | 
					                    z-index: var(--pf-global--ZIndex--lg);
 | 
				
			||||||
 | 
					                    --pf-c-page__header-tools--MarginRight: 0;
 | 
				
			||||||
 | 
					                    --ak-brand-logo-height: var(--pf-global--FontSize--4xl, 2.25rem);
 | 
				
			||||||
 | 
					                    --ak-brand-background-color: var(
 | 
				
			||||||
 | 
					                        --pf-c-page__sidebar--m-light--BackgroundColor
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                .bar {
 | 
					
 | 
				
			||||||
 | 
					                :host([theme="dark"]) {
 | 
				
			||||||
 | 
					                    --ak-brand-background-color: var(--pf-c-page__sidebar--BackgroundColor);
 | 
				
			||||||
 | 
					                    --pf-c-page__sidebar--BackgroundColor: var(--ak-dark-background-light);
 | 
				
			||||||
 | 
					                    color: var(--ak-dark-foreground);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                navbar {
 | 
				
			||||||
                    border-bottom: var(--pf-global--BorderWidth--sm);
 | 
					                    border-bottom: var(--pf-global--BorderWidth--sm);
 | 
				
			||||||
                    border-bottom-style: solid;
 | 
					                    border-bottom-style: solid;
 | 
				
			||||||
                    border-bottom-color: var(--pf-global--BorderColor--100);
 | 
					                    border-bottom-color: var(--pf-global--BorderColor--100);
 | 
				
			||||||
 | 
					                    background-color: var(--pf-c-page--BackgroundColor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    display: flex;
 | 
					                    display: flex;
 | 
				
			||||||
                    flex-direction: row;
 | 
					                    flex-direction: row;
 | 
				
			||||||
                    min-height: 114px;
 | 
					                    min-height: 6rem;
 | 
				
			||||||
                    max-height: 114px;
 | 
					
 | 
				
			||||||
                    background-color: var(--pf-c-page--BackgroundColor);
 | 
					                    display: grid;
 | 
				
			||||||
 | 
					                    row-gap: var(--pf-global--spacer--sm);
 | 
				
			||||||
 | 
					                    column-gap: var(--pf-global--spacer--sm);
 | 
				
			||||||
 | 
					                    grid-template-columns: [brand] auto [toggle] auto [primary] 1fr [secondary] auto;
 | 
				
			||||||
 | 
					                    grid-template-rows: auto auto;
 | 
				
			||||||
 | 
					                    grid-template-areas:
 | 
				
			||||||
 | 
					                        "brand toggle primary secondary"
 | 
				
			||||||
 | 
					                        "brand toggle description secondary";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    @media (max-width: 768px) {
 | 
				
			||||||
 | 
					                        row-gap: var(--pf-global--spacer--xs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        align-items: center;
 | 
				
			||||||
 | 
					                        grid-template-areas:
 | 
				
			||||||
 | 
					                            "toggle primary secondary"
 | 
				
			||||||
 | 
					                            "toggle description description";
 | 
				
			||||||
 | 
					                        justify-content: space-between;
 | 
				
			||||||
 | 
					                        width: 100%;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                .pf-c-page__main-section.pf-m-light {
 | 
					
 | 
				
			||||||
                    background-color: transparent;
 | 
					                .items {
 | 
				
			||||||
 | 
					                    display: block;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    &.primary {
 | 
				
			||||||
 | 
					                        grid-column: primary;
 | 
				
			||||||
 | 
					                        grid-row: primary / description;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        align-content: center;
 | 
				
			||||||
 | 
					                        padding-block: var(--pf-global--spacer--md);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        @media (min-width: 426px) {
 | 
				
			||||||
 | 
					                            &.block-sibling {
 | 
				
			||||||
 | 
					                                padding-block-end: 0;
 | 
				
			||||||
 | 
					                                grid-row: primary;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        @media (max-width: 768px) {
 | 
				
			||||||
 | 
					                            padding-block: var(--pf-global--spacer--sm);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        .accent-icon {
 | 
				
			||||||
 | 
					                            height: 1em;
 | 
				
			||||||
 | 
					                            width: 1em;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            @media (max-width: 768px) {
 | 
				
			||||||
 | 
					                                display: none;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    &.page-description {
 | 
				
			||||||
 | 
					                        grid-area: description;
 | 
				
			||||||
 | 
					                        padding-block-end: var(--pf-global--spacer--md);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        @media (max-width: 425px) {
 | 
				
			||||||
 | 
					                            display: none;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        @media (min-width: 769px) {
 | 
				
			||||||
 | 
					                            text-wrap: balance;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    &.secondary {
 | 
				
			||||||
 | 
					                        grid-area: secondary;
 | 
				
			||||||
 | 
					                        flex: 0 0 auto;
 | 
				
			||||||
 | 
					                        justify-self: end;
 | 
				
			||||||
 | 
					                        padding-block: var(--pf-global--spacer--sm);
 | 
				
			||||||
 | 
					                        padding-inline-end: var(--pf-global--spacer--sm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        @media (min-width: 769px) {
 | 
				
			||||||
 | 
					                            align-content: center;
 | 
				
			||||||
 | 
					                            padding-block: var(--pf-global--spacer--md);
 | 
				
			||||||
 | 
					                            padding-inline-end: var(--pf-global--spacer--xl);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                .pf-c-page__main-section {
 | 
					
 | 
				
			||||||
                    flex-grow: 1;
 | 
					                .brand {
 | 
				
			||||||
                    flex-shrink: 1;
 | 
					                    grid-area: brand;
 | 
				
			||||||
 | 
					                    background-color: var(--ak-brand-background-color);
 | 
				
			||||||
 | 
					                    height: 100%;
 | 
				
			||||||
 | 
					                    width: var(--pf-c-page__sidebar--Width);
 | 
				
			||||||
 | 
					                    align-items: center;
 | 
				
			||||||
 | 
					                    padding-inline: var(--pf-global--spacer--sm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    display: flex;
 | 
					                    display: flex;
 | 
				
			||||||
                    flex-direction: column;
 | 
					 | 
				
			||||||
                    justify-content: center;
 | 
					                    justify-content: center;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    &.pf-m-collapsed {
 | 
				
			||||||
 | 
					                        display: none;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    @media (max-width: 1279px) {
 | 
				
			||||||
 | 
					                        display: none;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                img.pf-icon {
 | 
					
 | 
				
			||||||
                    max-height: 24px;
 | 
					                .sidebar-trigger {
 | 
				
			||||||
 | 
					                    grid-area: toggle;
 | 
				
			||||||
 | 
					                    height: 100%;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                .logo {
 | 
				
			||||||
 | 
					                    flex: 0 0 auto;
 | 
				
			||||||
 | 
					                    height: var(--ak-brand-logo-height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    & img {
 | 
				
			||||||
 | 
					                        height: 100%;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                .sidebar-trigger,
 | 
					                .sidebar-trigger,
 | 
				
			||||||
                .notification-trigger {
 | 
					                .notification-trigger {
 | 
				
			||||||
                    font-size: 24px;
 | 
					                    font-size: 1.5rem;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                .notification-trigger.has-notifications {
 | 
					                .notification-trigger.has-notifications {
 | 
				
			||||||
                    color: var(--pf-global--active-color--100);
 | 
					                    color: var(--pf-global--active-color--100);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                .page-title {
 | 
				
			||||||
 | 
					                    display: flex;
 | 
				
			||||||
 | 
					                    gap: var(--pf-global--spacer--xs);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                h1 {
 | 
					                h1 {
 | 
				
			||||||
                    display: flex;
 | 
					                    display: flex;
 | 
				
			||||||
                    flex-direction: row;
 | 
					                    flex-direction: row;
 | 
				
			||||||
                    align-items: center !important;
 | 
					                    align-items: center !important;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                .pf-c-page__header-tools {
 | 
					 | 
				
			||||||
                    flex-shrink: 0;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                .pf-c-page__header-tools-group {
 | 
					 | 
				
			||||||
                    height: 100%;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                :host([theme="dark"]) .pf-c-page__header-tools {
 | 
					 | 
				
			||||||
                    color: var(--ak-dark-foreground) !important;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            `,
 | 
					            `,
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //#region Properties
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property({ type: String })
 | 
				
			||||||
 | 
					    icon?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property({ type: Boolean })
 | 
				
			||||||
 | 
					    iconImage = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property({ type: String })
 | 
				
			||||||
 | 
					    header?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property({ type: String })
 | 
				
			||||||
 | 
					    description?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property({ type: Boolean })
 | 
				
			||||||
 | 
					    hasIcon = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property({ type: Boolean })
 | 
				
			||||||
 | 
					    open = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @state()
 | 
				
			||||||
 | 
					    me?: SessionUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @state()
 | 
				
			||||||
 | 
					    uiConfig!: UIConfig;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //#endregion
 | 
				
			||||||
 | 
					    //#region Methods
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #toggleSidebar() {
 | 
				
			||||||
 | 
					        this.open = !this.open;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.dispatchEvent(
 | 
				
			||||||
 | 
					            new CustomEvent(EVENT_SIDEBAR_TOGGLE, {
 | 
				
			||||||
 | 
					                bubbles: true,
 | 
				
			||||||
 | 
					                composed: true,
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //#region Constructor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        super();
 | 
					        super();
 | 
				
			||||||
        window.addEventListener(EVENT_WS_MESSAGE, () => {
 | 
					        window.addEventListener(EVENT_WS_MESSAGE, () => {
 | 
				
			||||||
@ -119,13 +297,23 @@ export class PageHeader extends WithBrandConfig(AKElement) {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async firstUpdated() {
 | 
					    connectedCallback(): void {
 | 
				
			||||||
 | 
					        super.connectedCallback();
 | 
				
			||||||
 | 
					        AKPageNavbar.elementRef = this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    disconnectedCallback(): void {
 | 
				
			||||||
 | 
					        super.disconnectedCallback();
 | 
				
			||||||
 | 
					        AKPageNavbar.elementRef = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async firstUpdated() {
 | 
				
			||||||
        this.me = await me();
 | 
					        this.me = await me();
 | 
				
			||||||
        this.uiConfig = await uiConfig();
 | 
					        this.uiConfig = await uiConfig();
 | 
				
			||||||
        this.uiConfig.navbar.userDisplay = UserDisplay.none;
 | 
					        this.uiConfig.navbar.userDisplay = UserDisplay.none;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setTitle(header?: string) {
 | 
					    #setTitle(header?: string) {
 | 
				
			||||||
        const currentIf = currentInterface();
 | 
					        const currentIf = currentInterface();
 | 
				
			||||||
        let title = this.brand?.brandingTitle || TITLE_DEFAULT;
 | 
					        let title = this.brand?.brandingTitle || TITLE_DEFAULT;
 | 
				
			||||||
        if (currentIf === "admin") {
 | 
					        if (currentIf === "admin") {
 | 
				
			||||||
@ -141,65 +329,146 @@ export class PageHeader extends WithBrandConfig(AKElement) {
 | 
				
			|||||||
    willUpdate() {
 | 
					    willUpdate() {
 | 
				
			||||||
        // Always update title, even if there's no header value set,
 | 
					        // Always update title, even if there's no header value set,
 | 
				
			||||||
        // as in that case we still need to return to the generic title
 | 
					        // as in that case we still need to return to the generic title
 | 
				
			||||||
        this.setTitle(this.header);
 | 
					        this.#setTitle(this.header);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //#region Render
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    renderIcon() {
 | 
					    renderIcon() {
 | 
				
			||||||
        if (this.icon) {
 | 
					        if (this.icon) {
 | 
				
			||||||
            if (this.iconImage && !this.icon.startsWith("fa://")) {
 | 
					            if (this.iconImage && !this.icon.startsWith("fa://")) {
 | 
				
			||||||
                return html`<img class="pf-icon" src="${this.icon}" alt="page icon" />`;
 | 
					                return html`<img class="accent-icon pf-icon" src="${this.icon}" alt="page icon" />`;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const icon = this.icon.replaceAll("fa://", "fa ");
 | 
					            const icon = this.icon.replaceAll("fa://", "fa ");
 | 
				
			||||||
            return html`<i class=${icon}></i>`;
 | 
					
 | 
				
			||||||
 | 
					            return html`<i class="accent-icon ${icon}"></i>`;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return nothing;
 | 
					        return nothing;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render(): TemplateResult {
 | 
					    render(): TemplateResult {
 | 
				
			||||||
        return html`<div class="bar">
 | 
					        return html`<navbar aria-label="Main" class="navbar">
 | 
				
			||||||
            <button
 | 
					                <aside class="brand ${this.open ? "" : "pf-m-collapsed"}">
 | 
				
			||||||
                class="sidebar-trigger pf-c-button pf-m-plain"
 | 
					                    <a href="#/">
 | 
				
			||||||
                @click=${() => {
 | 
					                        <div class="logo">
 | 
				
			||||||
                    this.dispatchEvent(
 | 
					                            <img
 | 
				
			||||||
                        new CustomEvent(EVENT_SIDEBAR_TOGGLE, {
 | 
					                                src=${themeImage(
 | 
				
			||||||
                            bubbles: true,
 | 
					                                    this.brand?.brandingLogo ?? DefaultBrand.brandingLogo,
 | 
				
			||||||
                            composed: true,
 | 
					                                )}
 | 
				
			||||||
                        }),
 | 
					                                alt="${msg("authentik Logo")}"
 | 
				
			||||||
                    );
 | 
					                                loading="lazy"
 | 
				
			||||||
                }}
 | 
					                            />
 | 
				
			||||||
            >
 | 
					                        </div>
 | 
				
			||||||
                <i class="fas fa-bars"></i>
 | 
					                    </a>
 | 
				
			||||||
            </button>
 | 
					                </aside>
 | 
				
			||||||
            <section class="pf-c-page__main-section pf-m-light">
 | 
					                <button
 | 
				
			||||||
                <div class="pf-c-content">
 | 
					                    class="sidebar-trigger pf-c-button pf-m-plain"
 | 
				
			||||||
                    <h1>
 | 
					                    @click=${this.#toggleSidebar}
 | 
				
			||||||
 | 
					                    aria-label=${msg("Toggle sidebar")}
 | 
				
			||||||
 | 
					                    aria-expanded=${this.open ? "true" : "false"}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    <i class="fas fa-bars"></i>
 | 
				
			||||||
 | 
					                </button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <section
 | 
				
			||||||
 | 
					                    class="items primary pf-c-content ${this.description ? "block-sibling" : ""}"
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    <h1 class="page-title">
 | 
				
			||||||
                        ${this.hasIcon
 | 
					                        ${this.hasIcon
 | 
				
			||||||
                            ? html`<slot name="icon">${this.renderIcon()}</slot> `
 | 
					                            ? html`<slot name="icon">${this.renderIcon()}</slot>`
 | 
				
			||||||
                            : nothing}
 | 
					                            : nothing}
 | 
				
			||||||
                        <slot name="header">${this.header}</slot>
 | 
					                        ${this.header}
 | 
				
			||||||
                    </h1>
 | 
					                    </h1>
 | 
				
			||||||
                    ${this.description ? html`<p>${this.description}</p>` : html``}
 | 
					                </section>
 | 
				
			||||||
                </div>
 | 
					                ${this.description
 | 
				
			||||||
            </section>
 | 
					                    ? html`<section class="items page-description pf-c-content">
 | 
				
			||||||
            <div class="pf-c-page__header-tools">
 | 
					                          <p>${this.description}</p>
 | 
				
			||||||
                <div class="pf-c-page__header-tools-group">
 | 
					                      </section>`
 | 
				
			||||||
                    <ak-nav-buttons .uiConfig=${this.uiConfig} .me=${this.me}>
 | 
					                    : nothing}
 | 
				
			||||||
                        <a
 | 
					
 | 
				
			||||||
                            class="pf-c-button pf-m-secondary pf-m-small pf-u-display-none pf-u-display-block-on-md"
 | 
					                <section class="items secondary">
 | 
				
			||||||
                            href="${globalAK().api.base}if/user/"
 | 
					                    <div class="pf-c-page__header-tools-group">
 | 
				
			||||||
                            slot="extra"
 | 
					                        <ak-nav-buttons .uiConfig=${this.uiConfig} .me=${this.me}>
 | 
				
			||||||
                        >
 | 
					                            <a
 | 
				
			||||||
                            ${msg("User interface")}
 | 
					                                class="pf-c-button pf-m-secondary pf-m-small pf-u-display-none pf-u-display-block-on-md"
 | 
				
			||||||
                        </a>
 | 
					                                href="${globalAK().api.base}if/user/"
 | 
				
			||||||
                    </ak-nav-buttons>
 | 
					                                slot="extra"
 | 
				
			||||||
                </div>
 | 
					                            >
 | 
				
			||||||
            </div>
 | 
					                                ${msg("User interface")}
 | 
				
			||||||
        </div>`;
 | 
					                            </a>
 | 
				
			||||||
 | 
					                        </ak-nav-buttons>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </section>
 | 
				
			||||||
 | 
					            </navbar>
 | 
				
			||||||
 | 
					            <slot></slot>`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //#endregion
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//#region Page Header
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * A page header component, used to display the page title and description.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Internally, this component dispatches the `ak-page-header` event, which is
 | 
				
			||||||
 | 
					 * listened to by the `ak-page-navbar` component.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @singleton
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@customElement("ak-page-header")
 | 
				
			||||||
 | 
					export class AKPageHeader extends LitElement implements PageNavbarDetails {
 | 
				
			||||||
 | 
					    @property({ type: String })
 | 
				
			||||||
 | 
					    header?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property({ type: String })
 | 
				
			||||||
 | 
					    description?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property({ type: String })
 | 
				
			||||||
 | 
					    icon?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property({ type: Boolean })
 | 
				
			||||||
 | 
					    iconImage = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static get styles(): CSSResult[] {
 | 
				
			||||||
 | 
					        return [
 | 
				
			||||||
 | 
					            css`
 | 
				
			||||||
 | 
					                :host {
 | 
				
			||||||
 | 
					                    display: none;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            `,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    connectedCallback(): void {
 | 
				
			||||||
 | 
					        super.connectedCallback();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        AKPageNavbar.setNavbarDetails({
 | 
				
			||||||
 | 
					            header: this.header,
 | 
				
			||||||
 | 
					            description: this.description,
 | 
				
			||||||
 | 
					            icon: this.icon,
 | 
				
			||||||
 | 
					            iconImage: this.iconImage,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    updated(): void {
 | 
				
			||||||
 | 
					        AKPageNavbar.setNavbarDetails({
 | 
				
			||||||
 | 
					            header: this.header,
 | 
				
			||||||
 | 
					            description: this.description,
 | 
				
			||||||
 | 
					            icon: this.icon,
 | 
				
			||||||
 | 
					            iconImage: this.iconImage,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare global {
 | 
					declare global {
 | 
				
			||||||
    interface HTMLElementTagNameMap {
 | 
					    interface HTMLElementTagNameMap {
 | 
				
			||||||
        "ak-page-header": PageHeader;
 | 
					        "ak-page-header": AKPageHeader;
 | 
				
			||||||
 | 
					        "ak-page-navbar": AKPageNavbar;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -35,10 +35,7 @@ export class Sidebar extends AKElement {
 | 
				
			|||||||
                .pf-c-nav__section + .pf-c-nav__section {
 | 
					                .pf-c-nav__section + .pf-c-nav__section {
 | 
				
			||||||
                    --pf-c-nav__section--section--MarginTop: var(--pf-global--spacer--sm);
 | 
					                    --pf-c-nav__section--section--MarginTop: var(--pf-global--spacer--sm);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                .pf-c-nav__list .sidebar-brand {
 | 
					
 | 
				
			||||||
                    max-height: 82px;
 | 
					 | 
				
			||||||
                    margin-bottom: -0.5rem;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                nav {
 | 
					                nav {
 | 
				
			||||||
                    display: flex;
 | 
					                    display: flex;
 | 
				
			||||||
                    flex-direction: column;
 | 
					                    flex-direction: column;
 | 
				
			||||||
@ -70,7 +67,6 @@ export class Sidebar extends AKElement {
 | 
				
			|||||||
            class="pf-c-nav ${this.activeTheme === UiThemeEnum.Light ? "pf-m-light" : ""}"
 | 
					            class="pf-c-nav ${this.activeTheme === UiThemeEnum.Light ? "pf-m-light" : ""}"
 | 
				
			||||||
            aria-label=${msg("Global")}
 | 
					            aria-label=${msg("Global")}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
            <ak-sidebar-brand></ak-sidebar-brand>
 | 
					 | 
				
			||||||
            <ul class="pf-c-nav__list">
 | 
					            <ul class="pf-c-nav__list">
 | 
				
			||||||
                <slot></slot>
 | 
					                <slot></slot>
 | 
				
			||||||
            </ul>
 | 
					            </ul>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,3 @@
 | 
				
			|||||||
import { EVENT_SIDEBAR_TOGGLE } from "@goauthentik/common/constants";
 | 
					 | 
				
			||||||
import { AKElement } from "@goauthentik/elements/Base";
 | 
					import { AKElement } from "@goauthentik/elements/Base";
 | 
				
			||||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
 | 
					import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
 | 
				
			||||||
import { themeImage } from "@goauthentik/elements/utils/images";
 | 
					import { themeImage } from "@goauthentik/elements/utils/images";
 | 
				
			||||||
@ -42,22 +41,16 @@ export class SidebarBrand extends WithBrandConfig(AKElement) {
 | 
				
			|||||||
                    display: flex;
 | 
					                    display: flex;
 | 
				
			||||||
                    flex-direction: row;
 | 
					                    flex-direction: row;
 | 
				
			||||||
                    align-items: center;
 | 
					                    align-items: center;
 | 
				
			||||||
                    height: 114px;
 | 
					                    height: var(--ak-navbar-height);
 | 
				
			||||||
                    min-height: 114px;
 | 
					 | 
				
			||||||
                    border-bottom: var(--pf-global--BorderWidth--sm);
 | 
					                    border-bottom: var(--pf-global--BorderWidth--sm);
 | 
				
			||||||
                    border-bottom-style: solid;
 | 
					                    border-bottom-style: solid;
 | 
				
			||||||
                    border-bottom-color: var(--pf-global--BorderColor--100);
 | 
					                    border-bottom-color: var(--pf-global--BorderColor--100);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                .pf-c-brand img {
 | 
					                .pf-c-brand img {
 | 
				
			||||||
                    padding: 0 0.5rem;
 | 
					                    padding: 0 0.5rem;
 | 
				
			||||||
                    height: 42px;
 | 
					                    height: 42px;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                button.pf-c-button.sidebar-trigger {
 | 
					 | 
				
			||||||
                    background-color: transparent;
 | 
					 | 
				
			||||||
                    border-radius: 0px;
 | 
					 | 
				
			||||||
                    height: 100%;
 | 
					 | 
				
			||||||
                    color: var(--ak-dark-foreground);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            `,
 | 
					            `,
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -70,32 +63,15 @@ export class SidebarBrand extends WithBrandConfig(AKElement) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render(): TemplateResult {
 | 
					    render(): TemplateResult {
 | 
				
			||||||
        return html` ${window.innerWidth <= MIN_WIDTH
 | 
					        return html` <a href="#/" class="pf-c-page__header-brand-link">
 | 
				
			||||||
                ? html`
 | 
					            <div class="pf-c-brand ak-brand">
 | 
				
			||||||
                      <button
 | 
					                <img
 | 
				
			||||||
                          class="sidebar-trigger pf-c-button"
 | 
					                    src=${themeImage(this.brand?.brandingLogo ?? DefaultBrand.brandingLogo)}
 | 
				
			||||||
                          @click=${() => {
 | 
					                    alt="${msg("authentik Logo")}"
 | 
				
			||||||
                              this.dispatchEvent(
 | 
					                    loading="lazy"
 | 
				
			||||||
                                  new CustomEvent(EVENT_SIDEBAR_TOGGLE, {
 | 
					                />
 | 
				
			||||||
                                      bubbles: true,
 | 
					            </div>
 | 
				
			||||||
                                      composed: true,
 | 
					        </a>`;
 | 
				
			||||||
                                  }),
 | 
					 | 
				
			||||||
                              );
 | 
					 | 
				
			||||||
                          }}
 | 
					 | 
				
			||||||
                      >
 | 
					 | 
				
			||||||
                          <i class="fas fa-bars"></i>
 | 
					 | 
				
			||||||
                      </button>
 | 
					 | 
				
			||||||
                  `
 | 
					 | 
				
			||||||
                : html``}
 | 
					 | 
				
			||||||
            <a href="#/" class="pf-c-page__header-brand-link">
 | 
					 | 
				
			||||||
                <div class="pf-c-brand ak-brand">
 | 
					 | 
				
			||||||
                    <img
 | 
					 | 
				
			||||||
                        src=${themeImage(this.brand?.brandingLogo ?? DefaultBrand.brandingLogo)}
 | 
					 | 
				
			||||||
                        alt="${msg("authentik Logo")}"
 | 
					 | 
				
			||||||
                        loading="lazy"
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            </a>`;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user