Compare commits
38 Commits
npm-worksp
...
web/reques
Author | SHA1 | Date | |
---|---|---|---|
c8be337414 | |||
5c85c2c9e6 | |||
8248163958 | |||
9acebec1f6 | |||
2a96900dc7 | |||
ca42506fa0 | |||
34de6bfd3a | |||
2d94b16411 | |||
98503f6009 | |||
ac4ba5d9e2 | |||
f19ed14bf8 | |||
085debf170 | |||
cacdf64408 | |||
23665d173f | |||
272fdc516b | |||
b08dcc2289 | |||
c84be1d961 | |||
875fc5c735 | |||
66cefcc918 | |||
5d4c38032f | |||
7123b2c57b | |||
fc00bdee63 | |||
a056703da0 | |||
3f9502072d | |||
2d254d6a7e | |||
a7e3dca917 | |||
5d8408287f | |||
30beca9118 | |||
8946b81dbd | |||
db96e1a901 | |||
8b4e0361c4 | |||
22cb5b7379 | |||
2d0117d096 | |||
035bda4eac | |||
50906214e5 | |||
e505f274b6 | |||
fe52f44dca | |||
3146e5a50f |
2443
tests/wdio/package-lock.json
generated
2443
tests/wdio/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,8 +14,8 @@
|
|||||||
"build": "run-s build-locales esbuild:build",
|
"build": "run-s build-locales esbuild:build",
|
||||||
"build-proxy": "run-s build-locales esbuild:build-proxy",
|
"build-proxy": "run-s build-locales esbuild:build-proxy",
|
||||||
"watch": "run-s build-locales esbuild:watch",
|
"watch": "run-s build-locales esbuild:watch",
|
||||||
"lint": "cross-env NODE_OPTIONS='--max_old_space_size=16384' eslint . --max-warnings 0 --fix",
|
"lint": "cross-env NODE_OPTIONS='--max_old_space_size=65536' eslint . --max-warnings 0 --fix",
|
||||||
"lint:precommit": "cross-env NODE_OPTIONS='--max_old_space_size=16384' node scripts/eslint-precommit.mjs",
|
"lint:precommit": "cross-env NODE_OPTIONS='--max_old_space_size=65536' node scripts/eslint-precommit.mjs",
|
||||||
"lint:spelling": "node scripts/check-spelling.mjs",
|
"lint:spelling": "node scripts/check-spelling.mjs",
|
||||||
"lit-analyse": "lit-analyzer src",
|
"lit-analyse": "lit-analyzer src",
|
||||||
"precommit": "npm-run-all --parallel tsc lit-analyse lint:spelling --sequential lint:precommit prettier",
|
"precommit": "npm-run-all --parallel tsc lit-analyse lint:spelling --sequential lint:precommit prettier",
|
||||||
|
@ -12,4 +12,8 @@ export const authentikEnterpriseContext = createContext<LicenseSummary>(
|
|||||||
|
|
||||||
export const authentikBrandContext = createContext<CurrentBrand>(Symbol("authentik-brand-context"));
|
export const authentikBrandContext = createContext<CurrentBrand>(Symbol("authentik-brand-context"));
|
||||||
|
|
||||||
|
export const authentikLocalStoreContext = createContext<unknown>(
|
||||||
|
Symbol("authentik-local-store-context"),
|
||||||
|
);
|
||||||
|
|
||||||
export default authentikConfigContext;
|
export default authentikConfigContext;
|
||||||
|
31
web/src/elements/utils/tryCatch.ts
Normal file
31
web/src/elements/utils/tryCatch.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
type TryFn<T> = () => T;
|
||||||
|
type CatchFn<T> = (error: unknown) => T;
|
||||||
|
|
||||||
|
type TryCatchArgs<T> = {
|
||||||
|
tryFn: TryFn<T>;
|
||||||
|
catchFn?: CatchFn<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const isTryCatchArgs = <T>(t: any): t is TryCatchArgs<T> =>
|
||||||
|
typeof t === "object" && "tryFn" in t && "catchFn" in t;
|
||||||
|
|
||||||
|
export function tryCatch<T>({ tryFn, catchFn }: TryCatchArgs<T>): T;
|
||||||
|
export function tryCatch<T>(tryFn: TryFn<T>): T;
|
||||||
|
export function tryCatch<T>(tryFn: TryFn<T>, catchFn: CatchFn<T>): T;
|
||||||
|
export function tryCatch<T>(tryFn: TryFn<T> | TryCatchArgs<T>, catchFn?: CatchFn<T>): T {
|
||||||
|
if (isTryCatchArgs(tryFn)) {
|
||||||
|
catchFn = tryFn.catchFn;
|
||||||
|
tryFn = tryFn.tryFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (catchFn === undefined) {
|
||||||
|
catchFn = () => null as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return tryFn();
|
||||||
|
} catch (error) {
|
||||||
|
return catchFn(error);
|
||||||
|
}
|
||||||
|
}
|
95
web/src/user/LibraryPage/ApplicationCards.ts
Normal file
95
web/src/user/LibraryPage/ApplicationCards.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import { LayoutType } from "@goauthentik/common/ui/config";
|
||||||
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
|
|
||||||
|
import { css, html } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators.js";
|
||||||
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
|
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||||
|
import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";
|
||||||
|
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
||||||
|
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
|
|
||||||
|
import type { Application } from "@goauthentik/api";
|
||||||
|
|
||||||
|
import type { AppGroupEntry, AppGroupList } from "./types";
|
||||||
|
|
||||||
|
type Pair = [string, string];
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
const LAYOUTS = new Map<string, [string, string]>([
|
||||||
|
[
|
||||||
|
"row",
|
||||||
|
["pf-m-12-col", "pf-m-all-6-col-on-sm pf-m-all-4-col-on-md pf-m-all-5-col-on-lg pf-m-all-2-col-on-xl"]],
|
||||||
|
[
|
||||||
|
"2-column",
|
||||||
|
["pf-m-6-col", "pf-m-all-12-col-on-sm pf-m-all-12-col-on-md pf-m-all-4-col-on-lg pf-m-all-4-col-on-xl"],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"3-column",
|
||||||
|
["pf-m-4-col", "pf-m-all-12-col-on-sm pf-m-all-12-col-on-md pf-m-all-6-col-on-lg pf-m-all-6-col-on-xl"],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
@customElement("ak-library-application-cards")
|
||||||
|
export class LibraryPageApplicationCards extends AKElement {
|
||||||
|
static get styles() {
|
||||||
|
return [
|
||||||
|
PFBase,
|
||||||
|
PFEmptyState,
|
||||||
|
PFContent,
|
||||||
|
PFGrid,
|
||||||
|
css`
|
||||||
|
.app-group-header {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
margin-top: 1.2em;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@property({ attribute: true })
|
||||||
|
layout = "row" as LayoutType;
|
||||||
|
|
||||||
|
@property({ attribute: true })
|
||||||
|
background: string | undefined = undefined;
|
||||||
|
|
||||||
|
@property({ attribute: true })
|
||||||
|
selected = "";
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
apps: AppGroupList = [];
|
||||||
|
|
||||||
|
get currentLayout(): Pair {
|
||||||
|
const layout = LAYOUTS.get(this.layout);
|
||||||
|
if (!layout) {
|
||||||
|
console.warn(`Unrecognized layout: ${this.layout || "-undefined-"}`);
|
||||||
|
return LAYOUTS.get("row") as Pair;
|
||||||
|
}
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const [groupClass, groupGrid] = this.currentLayout;
|
||||||
|
|
||||||
|
return html`<div class="pf-l-grid pf-m-gutter">
|
||||||
|
${this.apps.map(([group, apps]: AppGroupEntry) => {
|
||||||
|
return html`<div class="pf-l-grid__item ${groupClass}">
|
||||||
|
<div class="pf-c-content app-group-header">
|
||||||
|
<h2>${group}</h2>
|
||||||
|
</div>
|
||||||
|
<div class="pf-l-grid pf-m-gutter ${groupGrid}">
|
||||||
|
${apps.map((app: Application) => {
|
||||||
|
return html`<ak-library-app
|
||||||
|
class="pf-l-grid__item"
|
||||||
|
.application=${app}
|
||||||
|
background=${ifDefined(this.background)}
|
||||||
|
?selected=${app.slug === this.selected}
|
||||||
|
></ak-library-app>`;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div> `;
|
||||||
|
})}
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
}
|
@ -1,48 +1,34 @@
|
|||||||
|
import { PFSize } from "@goauthentik/common/enums.js";
|
||||||
import { LayoutType } from "@goauthentik/common/ui/config";
|
import { LayoutType } from "@goauthentik/common/ui/config";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||||
|
import { UserInterface } from "@goauthentik/user/UserInterface";
|
||||||
|
|
||||||
import { css, html } from "lit";
|
import { msg } from "@lit/localize";
|
||||||
|
import { css, html, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
|
import { classMap } from "lit/directives/class-map.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||||
import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";
|
import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";
|
||||||
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
import PFTable from "@patternfly/patternfly/components/Table/table.css";
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
|
|
||||||
import type { Application } from "@goauthentik/api";
|
import type { Application } from "@goauthentik/api";
|
||||||
|
|
||||||
import type { AppGroupEntry, AppGroupList } from "./types";
|
import type { AppGroupEntry, AppGroupList } from "./types";
|
||||||
|
|
||||||
type Pair = [string, string];
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
const LAYOUTS = new Map<string, [string, string]>([
|
|
||||||
[
|
|
||||||
"row",
|
|
||||||
["pf-m-12-col", "pf-m-all-6-col-on-sm pf-m-all-4-col-on-md pf-m-all-5-col-on-lg pf-m-all-2-col-on-xl"]],
|
|
||||||
[
|
|
||||||
"2-column",
|
|
||||||
["pf-m-6-col", "pf-m-all-12-col-on-sm pf-m-all-12-col-on-md pf-m-all-4-col-on-lg pf-m-all-4-col-on-xl"],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"3-column",
|
|
||||||
["pf-m-4-col", "pf-m-all-12-col-on-sm pf-m-all-12-col-on-md pf-m-all-6-col-on-lg pf-m-all-6-col-on-xl"],
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
@customElement("ak-library-application-list")
|
@customElement("ak-library-application-list")
|
||||||
export class LibraryPageApplicationList extends AKElement {
|
export class LibraryPageApplicationList extends AKElement {
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [
|
return [
|
||||||
PFBase,
|
PFBase,
|
||||||
|
PFTable,
|
||||||
|
PFButton,
|
||||||
PFEmptyState,
|
PFEmptyState,
|
||||||
PFContent,
|
|
||||||
PFGrid,
|
|
||||||
css`
|
css`
|
||||||
.app-group-header {
|
.app-row a {
|
||||||
margin-bottom: 1em;
|
font-weight: bold;
|
||||||
margin-top: 1.2em;
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
@ -60,36 +46,110 @@ export class LibraryPageApplicationList extends AKElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
apps: AppGroupList = [];
|
apps: AppGroupList = [];
|
||||||
|
|
||||||
get currentLayout(): Pair {
|
expanded = new Set<string>();
|
||||||
const layout = LAYOUTS.get(this.layout);
|
|
||||||
if (!layout) {
|
|
||||||
console.warn(`Unrecognized layout: ${this.layout || "-undefined-"}`);
|
|
||||||
return LAYOUTS.get("row") as Pair;
|
|
||||||
}
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const [groupClass, groupGrid] = this.currentLayout;
|
const me = rootInterface<UserInterface>()?.me;
|
||||||
|
const canEdit =
|
||||||
|
rootInterface()?.uiConfig?.enabledFeatures.applicationEdit && me?.user.isSuperuser;
|
||||||
|
|
||||||
return html`<div class="pf-l-grid pf-m-gutter">
|
const toggleExpansion = (pk: string) => {
|
||||||
${this.apps.map(([group, apps]: AppGroupEntry) => {
|
if (this.expanded.has(pk)) {
|
||||||
return html`<div class="pf-l-grid__item ${groupClass}">
|
this.expanded.delete(pk);
|
||||||
<div class="pf-c-content app-group-header">
|
} else {
|
||||||
<h2>${group}</h2>
|
this.expanded.add(pk);
|
||||||
|
}
|
||||||
|
this.requestUpdate();
|
||||||
|
};
|
||||||
|
|
||||||
|
const expandedClass = (pk: string) => ({
|
||||||
|
"pf-m-expanded": this.expanded.has(pk),
|
||||||
|
});
|
||||||
|
|
||||||
|
const renderExpansionCell = (app: Application) =>
|
||||||
|
app.metaDescription
|
||||||
|
? html`<td class="pf-c-table__toggle" role="cell">
|
||||||
|
<button
|
||||||
|
class="pf-c-button pf-m-plain ${classMap(expandedClass(app.pk))}"
|
||||||
|
@click=${() => toggleExpansion(app.pk)}
|
||||||
|
>
|
||||||
|
<div class="pf-c-table__toggle-icon">
|
||||||
|
<i class="fas fa-angle-down" aria-hidden="true"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-l-grid pf-m-gutter ${groupGrid}">
|
</button>
|
||||||
${apps.map((app: Application) => {
|
</td>`
|
||||||
return html`<ak-library-app
|
: nothing;
|
||||||
class="pf-l-grid__item"
|
|
||||||
.application=${app}
|
const renderAppIcon = (app: Application) =>
|
||||||
background=${ifDefined(this.background)}
|
html`<a
|
||||||
?selected=${app.slug === this.selected}
|
href="${ifDefined(app.launchUrl ?? "")}"
|
||||||
></ak-library-app>`;
|
target="${ifDefined(app.openInNewTab ? "_blank" : undefined)}"
|
||||||
})}
|
>
|
||||||
</div>
|
<ak-app-icon size=${PFSize.Small} .app=${app}></ak-app-icon>
|
||||||
</div> `;
|
</a>`;
|
||||||
})}
|
|
||||||
</div>`;
|
const renderAppUrl = (app: Application) =>
|
||||||
|
app.launchUrl === "goauthentik.io://providers/rac/launch"
|
||||||
|
? html`<ak-library-rac-endpoint-launch .app=${app}>
|
||||||
|
<a slot="trigger"> ${app.name} </a>
|
||||||
|
</ak-library-rac-endpoint-launch>`
|
||||||
|
: html`<a
|
||||||
|
href="${ifDefined(app.launchUrl ?? "")}"
|
||||||
|
target="${ifDefined(app.openInNewTab ? "_blank" : undefined)}"
|
||||||
|
>${app.name}</a
|
||||||
|
>`;
|
||||||
|
|
||||||
|
const renderAppDescription = (app: Application) =>
|
||||||
|
app.metaDescription
|
||||||
|
? html` <tr
|
||||||
|
class="pf-c-table__expandable-row ${classMap(expandedClass(app.pk))}"
|
||||||
|
role="row"
|
||||||
|
>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td colspan="3">${app.metaDescription}</td>
|
||||||
|
</tr>`
|
||||||
|
: nothing;
|
||||||
|
|
||||||
|
const renderGroup = ([group, apps]: AppGroupEntry) => html`
|
||||||
|
${group
|
||||||
|
? html`<tr>
|
||||||
|
<td colspan="5"><h2>${group}</h2></td>
|
||||||
|
</tr>`
|
||||||
|
: nothing}
|
||||||
|
${apps.map(
|
||||||
|
(app: Application) =>
|
||||||
|
html`<tr>
|
||||||
|
<td>${renderExpansionCell(app)}</td>
|
||||||
|
<td>${renderAppIcon(app)}</td>
|
||||||
|
<td class="app-row">${renderAppUrl(app)}</td>
|
||||||
|
<td>${app.metaPublisher ?? ""}</td>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
class="pf-c-button pf-m-control pf-m-small pf-m-block"
|
||||||
|
href="/if/admin/#/core/applications/${app?.slug}"
|
||||||
|
>
|
||||||
|
<i class="fas fa-edit"></i> ${msg("Edit")}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
${this.expanded.has(app.pk) ? renderAppDescription(app) : nothing} `,
|
||||||
|
)}
|
||||||
|
`;
|
||||||
|
|
||||||
|
return html`<table class="pf-c-table pf-m-compact pf-m-grid-sm pf-m-expandable">
|
||||||
|
<thead>
|
||||||
|
<tr role="row">
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
<th>${msg("Application")}</th>
|
||||||
|
<th>${msg("Publisher")}</th>
|
||||||
|
${canEdit ? html`<th>${msg("Edit")}</th>` : nothing}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
${this.apps.map(renderGroup)}
|
||||||
|
</tbody>
|
||||||
|
</table> `;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,30 @@ export const styles = [PFBase, PFDisplay, PFEmptyState, PFPage, PFContent].conca
|
|||||||
.pf-c-page__main-section {
|
.pf-c-page__main-section {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#library-page-title {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#library-page-title h1 {
|
||||||
|
padding-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#library-page-title i {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#library-page-title i[checked] {
|
||||||
|
border: 3px solid var(--pf-global--BorderColor--100);
|
||||||
|
}
|
||||||
|
|
||||||
|
#library-page-title a,
|
||||||
|
#library-page-title i {
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
export default styles;
|
export default styles;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { groupBy } from "@goauthentik/common/utils";
|
import { groupBy } from "@goauthentik/common/utils";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
import "@goauthentik/elements/EmptyState";
|
import "@goauthentik/elements/EmptyState";
|
||||||
|
import { tryCatch } from "@goauthentik/elements/utils/tryCatch.js";
|
||||||
import "@goauthentik/user/LibraryApplication";
|
import "@goauthentik/user/LibraryApplication";
|
||||||
|
|
||||||
import { msg } from "@lit/localize";
|
import { msg } from "@lit/localize";
|
||||||
@ -12,6 +13,7 @@ import styles from "./LibraryPageImpl.css";
|
|||||||
|
|
||||||
import type { Application } from "@goauthentik/api";
|
import type { Application } from "@goauthentik/api";
|
||||||
|
|
||||||
|
import "./ApplicationCards";
|
||||||
import "./ApplicationEmptyState";
|
import "./ApplicationEmptyState";
|
||||||
import "./ApplicationList";
|
import "./ApplicationList";
|
||||||
import "./ApplicationSearch";
|
import "./ApplicationSearch";
|
||||||
@ -20,6 +22,8 @@ import { SEARCH_ITEM_SELECTED, SEARCH_UPDATED } from "./constants";
|
|||||||
import { isCustomEvent, loading } from "./helpers";
|
import { isCustomEvent, loading } from "./helpers";
|
||||||
import type { AppGroupList, PageUIConfig } from "./types";
|
import type { AppGroupList, PageUIConfig } from "./types";
|
||||||
|
|
||||||
|
const VIEW_KEY = "ak-library-page-view-preference";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of Applications available
|
* List of Applications available
|
||||||
*
|
*
|
||||||
@ -53,6 +57,9 @@ export class LibraryPage extends AKElement {
|
|||||||
@state()
|
@state()
|
||||||
filteredApps: Application[] = [];
|
filteredApps: Application[] = [];
|
||||||
|
|
||||||
|
@property()
|
||||||
|
viewPreference?: string;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.searchUpdated = this.searchUpdated.bind(this);
|
this.searchUpdated = this.searchUpdated.bind(this);
|
||||||
@ -66,6 +73,12 @@ export class LibraryPage extends AKElement {
|
|||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this.filteredApps = this.apps;
|
this.filteredApps = this.apps;
|
||||||
|
this.viewPreference =
|
||||||
|
this.viewPreference ??
|
||||||
|
tryCatch(
|
||||||
|
() => window.localStorage.getItem(VIEW_KEY) ?? undefined,
|
||||||
|
(e) => "card",
|
||||||
|
);
|
||||||
if (this.filteredApps === undefined) {
|
if (this.filteredApps === undefined) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Application.results should never be undefined when passed to the Library Page.",
|
"Application.results should never be undefined when passed to the Library Page.",
|
||||||
@ -110,6 +123,13 @@ export class LibraryPage extends AKElement {
|
|||||||
return groupBy(this.filteredApps.filter(appHasLaunchUrl), (app) => app.group || "");
|
return groupBy(this.filteredApps.filter(appHasLaunchUrl), (app) => app.group || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setView(view: string) {
|
||||||
|
this.viewPreference = view;
|
||||||
|
tryCatch(() => {
|
||||||
|
window.localStorage.setItem(VIEW_KEY, view);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
renderEmptyState() {
|
renderEmptyState() {
|
||||||
return html`<ak-library-application-empty-list
|
return html`<ak-library-application-empty-list
|
||||||
?isadmin=${this.isAdmin}
|
?isadmin=${this.isAdmin}
|
||||||
@ -122,12 +142,17 @@ export class LibraryPage extends AKElement {
|
|||||||
const layout = this.uiConfig.layout as string;
|
const layout = this.uiConfig.layout as string;
|
||||||
const background = this.uiConfig.background;
|
const background = this.uiConfig.background;
|
||||||
|
|
||||||
return html`<ak-library-application-list
|
return this.viewPreference === "list"
|
||||||
|
? html`<ak-library-application-list
|
||||||
|
selected="${ifDefined(selected)}"
|
||||||
|
.apps=${apps}
|
||||||
|
></ak-library-application-list>`
|
||||||
|
: html`<ak-library-application-cards
|
||||||
layout="${layout}"
|
layout="${layout}"
|
||||||
background="${ifDefined(background)}"
|
background="${ifDefined(background)}"
|
||||||
selected="${ifDefined(selected)}"
|
selected="${ifDefined(selected)}"
|
||||||
.apps=${apps}
|
.apps=${apps}
|
||||||
></ak-library-application-list>`;
|
></ak-library-application-cards>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSearch() {
|
renderSearch() {
|
||||||
@ -137,9 +162,15 @@ export class LibraryPage extends AKElement {
|
|||||||
render() {
|
render() {
|
||||||
return html`<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
|
return html`<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
|
||||||
<div class="pf-c-content header">
|
<div class="pf-c-content header">
|
||||||
<h1 role="heading" aria-level="1" id="library-page-title">
|
<div id="library-page-title">
|
||||||
${msg("My applications")}
|
<h1 role="heading" aria-level="1">${msg("My applications")}</h1>
|
||||||
</h1>
|
<a id="card-view" @click=${() => this.setView("card")}
|
||||||
|
><i ?checked=${this.viewPreference === "card"} class="fas fa-th-large"></i
|
||||||
|
></a>
|
||||||
|
<a id="list-view" @click=${() => this.setView("list")}
|
||||||
|
><i ?checked=${this.viewPreference === "list"} class="fas fa-list"></i
|
||||||
|
></a>
|
||||||
|
</div>
|
||||||
${this.uiConfig.searchEnabled ? this.renderSearch() : html``}
|
${this.uiConfig.searchEnabled ? this.renderSearch() : html``}
|
||||||
</div>
|
</div>
|
||||||
<section class="pf-c-page__main-section">
|
<section class="pf-c-page__main-section">
|
||||||
|
Reference in New Issue
Block a user