web/elements: add grouping and descriptions to search select
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
ce5d1fd80d
commit
6d86067cea
|
@ -1,3 +1,4 @@
|
||||||
|
import { groupBy } from "@goauthentik/common/utils";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
|
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
|
@ -44,23 +45,33 @@ export class SearchSelect<T> extends AKElement {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
renderElement!: (element: T) => string;
|
renderElement!: (element: T) => string;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
renderDescription?: (element: T) => TemplateResult;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
value!: (element: T | undefined) => unknown;
|
value!: (element: T | undefined) => unknown;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
selected!: (element: T) => boolean;
|
selected?: (element: T) => boolean;
|
||||||
|
|
||||||
firstUpdated(): void {
|
firstUpdated(): void {
|
||||||
this.fetchObjects(this.query).then((objects) => {
|
this.fetchObjects(this.query).then((objects) => {
|
||||||
this.objects = objects;
|
this.objects = objects;
|
||||||
this.objects.forEach((obj) => {
|
this.objects.forEach((obj) => {
|
||||||
if (this.selected(obj)) {
|
if (this.selected && this.selected(obj)) {
|
||||||
this.selectedObject = obj;
|
this.selectedObject = obj;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
groupBy: (items: T[]) => [string, T[]][] = (items: T[]): [string, T[]][] => {
|
||||||
|
return groupBy(items, () => {
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
menuId: string;
|
menuId: string;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -86,6 +97,50 @@ export class SearchSelect<T> extends AKElement {
|
||||||
*/
|
*/
|
||||||
renderMenu(): void {
|
renderMenu(): void {
|
||||||
const pos = this.getBoundingClientRect();
|
const pos = this.getBoundingClientRect();
|
||||||
|
let groupedItems = this.groupBy(this.objects);
|
||||||
|
let shouldRenderGroups = true;
|
||||||
|
if (groupedItems.length === 1) {
|
||||||
|
if (groupedItems[0].length < 1 || groupedItems[0][0] === "") {
|
||||||
|
shouldRenderGroups = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (groupedItems.length === 0) {
|
||||||
|
shouldRenderGroups = false;
|
||||||
|
groupedItems = [["", []]];
|
||||||
|
}
|
||||||
|
const renderGroup = (items: T[]): TemplateResult => {
|
||||||
|
return html`${items.map((obj) => {
|
||||||
|
let desc = undefined;
|
||||||
|
if (this.renderDescription) {
|
||||||
|
desc = this.renderDescription(obj);
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
class="pf-c-dropdown__menu-item ${desc === undefined
|
||||||
|
? ""
|
||||||
|
: "pf-m-description"}"
|
||||||
|
role="option"
|
||||||
|
@click=${() => {
|
||||||
|
this.selectedObject = obj;
|
||||||
|
this.open = false;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
${desc === undefined
|
||||||
|
? this.renderElement(obj)
|
||||||
|
: html`
|
||||||
|
<div class="pf-c-dropdown__menu-item-main">
|
||||||
|
${this.renderElement(obj)}
|
||||||
|
</div>
|
||||||
|
<div class="pf-c-dropdown__menu-item-description">
|
||||||
|
${desc}
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
`;
|
||||||
|
})}`;
|
||||||
|
};
|
||||||
render(
|
render(
|
||||||
html`<div
|
html`<div
|
||||||
class="pf-c-dropdown pf-m-expanded"
|
class="pf-c-dropdown pf-m-expanded"
|
||||||
|
@ -97,7 +152,7 @@ export class SearchSelect<T> extends AKElement {
|
||||||
<ul class="pf-c-dropdown__menu pf-m-static" role="listbox">
|
<ul class="pf-c-dropdown__menu pf-m-static" role="listbox">
|
||||||
${this.blankable
|
${this.blankable
|
||||||
? html`
|
? html`
|
||||||
<li role="presentation">
|
<li>
|
||||||
<button
|
<button
|
||||||
class="pf-c-dropdown__menu-item"
|
class="pf-c-dropdown__menu-item"
|
||||||
role="option"
|
role="option"
|
||||||
|
@ -111,22 +166,18 @@ export class SearchSelect<T> extends AKElement {
|
||||||
</li>
|
</li>
|
||||||
`
|
`
|
||||||
: html``}
|
: html``}
|
||||||
${this.objects.map((obj) => {
|
${shouldRenderGroups
|
||||||
return html`
|
? html`${groupedItems.map(([group, items]) => {
|
||||||
<li role="presentation">
|
return html`
|
||||||
<button
|
<section class="pf-c-dropdown__group">
|
||||||
class="pf-c-dropdown__menu-item"
|
<h1 class="pf-c-dropdown__group-title">${group}</h1>
|
||||||
role="option"
|
<ul>
|
||||||
@click=${() => {
|
${renderGroup(items)}
|
||||||
this.selectedObject = obj;
|
</ul>
|
||||||
this.open = false;
|
</section>
|
||||||
}}
|
`;
|
||||||
>
|
})}`
|
||||||
${this.renderElement(obj)}
|
: html`${renderGroup(groupedItems[0][1])}`}
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>`,
|
</div>`,
|
||||||
document.body,
|
document.body,
|
||||||
|
|
Reference in New Issue