web/elements: render ak-seach-select dropdown correctly in modals
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
		| @ -13,6 +13,7 @@ | ||||
|         <link rel="stylesheet" type="text/css" href="{% static 'dist/page.css' %}"> | ||||
|         <link rel="stylesheet" type="text/css" href="{% static 'dist/empty-state.css' %}"> | ||||
|         <link rel="stylesheet" type="text/css" href="{% static 'dist/spinner.css' %}"> | ||||
|         <link rel="stylesheet" type="text/css" href="{% static 'dist/dropdown.css' %}"> | ||||
|         {% block head_before %} | ||||
|         {% endblock %} | ||||
|         <link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}"> | ||||
|  | ||||
| @ -24,6 +24,10 @@ export const resources = [ | ||||
|         src: "node_modules/@patternfly/patternfly/patternfly-base.css", | ||||
|         dest: "dist/", | ||||
|     }, | ||||
|     { | ||||
|         src: "node_modules/@patternfly/patternfly/components/Dropdown/dropdown.css", | ||||
|         dest: "dist/", | ||||
|     }, | ||||
|     { | ||||
|         src: "node_modules/@patternfly/patternfly/components/Page/page.css", | ||||
|         dest: "dist/", | ||||
|  | ||||
| @ -310,6 +310,18 @@ html > form > input { | ||||
|     .pf-c-dropdown__toggle::before { | ||||
|         border-color: transparent; | ||||
|     } | ||||
|     .pf-c-dropdown__menu { | ||||
|         --pf-c-dropdown__menu--BackgroundColor: var(--ak-dark-background); | ||||
|     } | ||||
|     .pf-c-dropdown__menu-item { | ||||
|         --pf-c-dropdown__menu-item--BackgroundColor: var(--ak-dark-background); | ||||
|         --pf-c-dropdown__menu-item--Color: var(--ak-dark-foreground); | ||||
|     } | ||||
|     .pf-c-dropdown__menu-item:hover, | ||||
|     .pf-c-dropdown__menu-item:focus { | ||||
|         --pf-c-dropdown__menu-item--BackgroundColor: var(--ak-dark-background-light-ish); | ||||
|         --pf-c-dropdown__menu-item--Color: var(--ak-dark-foreground); | ||||
|     } | ||||
|     .pf-c-toggle-group__button { | ||||
|         color: var(--ak-dark-foreground) !important; | ||||
|     } | ||||
|  | ||||
| @ -2,7 +2,7 @@ import { AKElement } from "@goauthentik/elements/Base"; | ||||
|  | ||||
| import { t } from "@lingui/macro"; | ||||
|  | ||||
| import { CSSResult, TemplateResult, html } from "lit"; | ||||
| import { CSSResult, TemplateResult, html, render } from "lit"; | ||||
| import { customElement, property } from "lit/decorators.js"; | ||||
|  | ||||
| import AKGlobal from "@goauthentik/common/styles/authentik.css"; | ||||
| @ -61,37 +61,45 @@ export class SearchSelect<T> extends AKElement { | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     render(): TemplateResult { | ||||
|         return html`<div class="pf-c-select"> | ||||
|             <div class="pf-c-select__toggle pf-m-typeahead"> | ||||
|                 <div class="pf-c-select__toggle-wrapper"> | ||||
|                     <input | ||||
|                         class="pf-c-form-control pf-c-select__toggle-typeahead" | ||||
|                         type="text" | ||||
|                         placeholder=${this.placeholder} | ||||
|                         @input=${(ev: InputEvent) => { | ||||
|                             this.query = (ev.target as HTMLInputElement).value; | ||||
|                             this.firstUpdated(); | ||||
|                         }} | ||||
|                         @focus=${() => { | ||||
|                             this.open = true; | ||||
|                         }} | ||||
|                         @blur=${() => { | ||||
|                             setTimeout(() => { | ||||
|                                 this.open = false; | ||||
|                             }, 200); | ||||
|                         }} | ||||
|                         .value=${this.selectedObject ? this.renderElement(this.selectedObject) : ""} | ||||
|                     /> | ||||
|                 </div> | ||||
|             </div> | ||||
|     menuId: string; | ||||
|  | ||||
|             <ul class="pf-c-select__menu" role="listbox" ?hidden="${!this.open}"> | ||||
|     constructor() { | ||||
|         super(); | ||||
|         this.menuId = btoa(Math.random().toString()).substring(10, 15); | ||||
|     } | ||||
|  | ||||
|     disconnectedCallback(): void { | ||||
|         super.disconnectedCallback(); | ||||
|         document.querySelectorAll(`#${this.menuId}`).forEach((e) => e.remove()); | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * This is a little bit hacky. Because we mainly want to use this field in modal-based forms, | ||||
|      * rendering this menu inline makes the menu not overlay over top of the modal, and cause | ||||
|      * the modal to scroll. | ||||
|      * Hence, we render the menu into the document root, hide it when this menu isn't open | ||||
|      * and remove it on disconnect | ||||
|      * Also to move it to the correct position we're getting this elements's position and use that | ||||
|      * to position the menu | ||||
|      * The other downside this has is that, since we're rendering outside of a shadow root, | ||||
|      * the pf-c-dropdown CSS needs to be loaded on the body. | ||||
|      */ | ||||
|     renderMenu(): void { | ||||
|         const pos = this.getBoundingClientRect(); | ||||
|         render( | ||||
|             html`<div | ||||
|                 class="pf-c-dropdown pf-m-expanded" | ||||
|                 ?hidden=${!this.open} | ||||
|                 id="${this.menuId}" | ||||
|                 style="position: fixed; inset: 0px auto auto 0px; z-index: 9999; transform: translate(${pos.x}px, ${pos.y + | ||||
|                 this.offsetHeight}px); width: ${pos.width}px;" | ||||
|             > | ||||
|                 <ul class="pf-c-dropdown__menu pf-m-static" role="listbox"> | ||||
|                     ${this.blankable | ||||
|                         ? html` | ||||
|                               <li role="presentation"> | ||||
|                                   <button | ||||
|                                   class="pf-c-select__menu-item" | ||||
|                                       class="pf-c-dropdown__menu-item" | ||||
|                                       role="option" | ||||
|                                       @click=${() => { | ||||
|                                           this.selectedObject = undefined; | ||||
| @ -107,7 +115,7 @@ export class SearchSelect<T> extends AKElement { | ||||
|                         return html` | ||||
|                             <li role="presentation"> | ||||
|                                 <button | ||||
|                                 class="pf-c-select__menu-item" | ||||
|                                     class="pf-c-dropdown__menu-item" | ||||
|                                     role="option" | ||||
|                                     @click=${() => { | ||||
|                                         this.selectedObject = obj; | ||||
| @ -120,6 +128,39 @@ export class SearchSelect<T> extends AKElement { | ||||
|                         `; | ||||
|                     })} | ||||
|                 </ul> | ||||
|             </div>`, | ||||
|             document.body, | ||||
|             { host: this }, | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     render(): TemplateResult { | ||||
|         this.renderMenu(); | ||||
|         return html`<div class="pf-c-select"> | ||||
|             <div class="pf-c-select__toggle pf-m-typeahead"> | ||||
|                 <div class="pf-c-select__toggle-wrapper"> | ||||
|                     <input | ||||
|                         class="pf-c-form-control pf-c-select__toggle-typeahead" | ||||
|                         type="text" | ||||
|                         placeholder=${this.placeholder} | ||||
|                         @input=${(ev: InputEvent) => { | ||||
|                             this.query = (ev.target as HTMLInputElement).value; | ||||
|                             this.firstUpdated(); | ||||
|                         }} | ||||
|                         @focus=${() => { | ||||
|                             this.open = true; | ||||
|                             this.renderMenu(); | ||||
|                         }} | ||||
|                         @blur=${() => { | ||||
|                             setTimeout(() => { | ||||
|                                 this.open = false; | ||||
|                                 this.renderMenu(); | ||||
|                             }, 200); | ||||
|                         }} | ||||
|                         .value=${this.selectedObject ? this.renderElement(this.selectedObject) : ""} | ||||
|                     /> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div>`; | ||||
|     } | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Jens Langhammer
					Jens Langhammer