mirror of
https://github.com/goauthentik/authentik
synced 2026-05-14 19:06:39 +02:00
Compare commits
10 Commits
fix-make-a
...
minimal-mo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6047c63e6f | ||
|
|
af35192e1f | ||
|
|
85138c11d9 | ||
|
|
e5d2f08746 | ||
|
|
5c12a44462 | ||
|
|
5307617b61 | ||
|
|
a24ffecba0 | ||
|
|
383a3bad38 | ||
|
|
47f6631c30 | ||
|
|
d7b801598a |
@@ -1,10 +1,16 @@
|
||||
import { Config, ConfigFromJSON, CurrentBrand, CurrentBrandFromJSON } from "@goauthentik/api";
|
||||
import {
|
||||
Config,
|
||||
ConfigFromJSON,
|
||||
CurrentBrand,
|
||||
CurrentBrandFromJSON,
|
||||
FlowLayoutEnum,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
export interface GlobalAuthentik {
|
||||
_converted?: boolean;
|
||||
locale?: string;
|
||||
flow?: {
|
||||
layout: string;
|
||||
layout: FlowLayoutEnum;
|
||||
};
|
||||
config: Config;
|
||||
brand: CurrentBrand;
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
--ak-dark-background-light-ish: #212427;
|
||||
--ak-dark-background-lighter: #2b2e33;
|
||||
|
||||
--ak-flow-background-color-contrast: var(--pf-global--Color--100);
|
||||
--ak-flow-footer-color: var(--pf-global--Color--light-100);
|
||||
|
||||
/* PatternFly likes to override global variables for some reason */
|
||||
--ak-global--Color--100: var(--pf-global--Color--100);
|
||||
|
||||
@@ -311,27 +314,38 @@ ak-tabs[vertical] {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
flex: 1 1 auto;
|
||||
place-content: center;
|
||||
}
|
||||
|
||||
@media (max-width: 1199px) {
|
||||
.pf-c-login__container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.ak-login-container {
|
||||
height: calc(100vh - var(--pf-global--spacer--lg) - var(--pf-global--spacer--lg));
|
||||
width: 35rem;
|
||||
max-width: 35rem;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.pf-c-login__header {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.pf-c-login__footer {
|
||||
flex-grow: 2;
|
||||
color: var(--ak-flow-footer-color);
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
:root {
|
||||
--ak-flow-footer-color: var(--ak-flow-background-color-contrast);
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-login__footer ul.pf-c-list.pf-m-inline {
|
||||
justify-content: center;
|
||||
padding: 2rem 0;
|
||||
@@ -539,11 +553,16 @@ fieldset {
|
||||
}
|
||||
|
||||
@media (min-height: 60rem) {
|
||||
.pf-c-login.stacked .pf-c-login__main {
|
||||
.pf-c-login[data-layout="stacked"] .pf-c-login__main {
|
||||
margin-top: 13rem;
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-login[data-layout="sidebar_left"],
|
||||
.pf-c-login[data-layout="sidebar_right"] {
|
||||
--ak-flow-footer-color: var(--ak-flow-background-color-contrast);
|
||||
}
|
||||
|
||||
.pf-c-data-list {
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
|
||||
@@ -394,11 +394,13 @@ select.pf-c-form-control {
|
||||
}
|
||||
|
||||
.pf-c-notification-drawer {
|
||||
--pf-c-notification-drawer--BackgroundColor: var(--ak-dark-background);
|
||||
--pf-c-notification-drawer--BackgroundColor: var(--ak-dark-background) !important;
|
||||
--pf-c-notification-drawer__header--BackgroundColor: var(
|
||||
--ak-dark-background-lighter
|
||||
) !important;
|
||||
}
|
||||
|
||||
.pf-c-notification-drawer__header {
|
||||
background-color: var(--ak-dark-background-lighter);
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,8 +66,13 @@ export class AppIcon extends AKElement implements IAppIcon {
|
||||
padding: var(--icon-border);
|
||||
max-height: calc(var(--icon-height) + var(--icon-border) + var(--icon-border));
|
||||
line-height: calc(var(--icon-height) + var(--icon-border) + var(--icon-border));
|
||||
filter: drop-shadow(5px 5px 5px rgba(128, 128, 128, 0.25));
|
||||
filter: drop-shadow(hsl(0deg 0% 85%) 5px 5px 5px);
|
||||
}
|
||||
|
||||
:host([theme="dark"]) .icon {
|
||||
filter: drop-shadow(hsl(0deg 0% 11%) 5px 5px 4px);
|
||||
}
|
||||
|
||||
div {
|
||||
height: calc(var(--icon-height) + var(--icon-border) + var(--icon-border));
|
||||
}
|
||||
@@ -77,14 +82,14 @@ export class AppIcon extends AKElement implements IAppIcon {
|
||||
render(): TemplateResult {
|
||||
// prettier-ignore
|
||||
return match([this.name, this.icon])
|
||||
.with([undefined, undefined],
|
||||
() => html`<div><i class="icon fas fa-question-circle" aria-hidden="true"></i></div>`)
|
||||
.with([P.nullish, P.nullish],
|
||||
() => html`<div><i aria-hidden="true" class="icon fas fa-question-circle"></i></div>`)
|
||||
.with([P._, P.string.startsWith("fa://")],
|
||||
([_name, icon]) => html`<div><i class="icon fas ${icon.replaceAll("fa://", "")}"></i></div>`)
|
||||
([_name, icon]) => html`<div><i aria-hidden="true" class="icon fas ${icon.replaceAll("fa://", "")}"></i></div>`)
|
||||
.with([P._, P.string],
|
||||
([_name, icon]) => html`<img class="icon pf-c-avatar" src="${icon}" alt="${msg("Application Icon")}" />`)
|
||||
.with([P.string, undefined],
|
||||
([name]) => html`<span class="icon">${name.charAt(0).toUpperCase()}</span>`)
|
||||
([_name, icon]) => html`<img aria-hidden="true" class="icon pf-c-avatar" src="${icon}" alt="${msg("Application Icon")}" />`)
|
||||
.with([P.string, P.nullish],
|
||||
([name]) => html`<span aria-hidden="true" class="icon">${name.charAt(0).toUpperCase()}</span>`)
|
||||
.exhaustive();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,20 +19,24 @@ export interface IExpand {
|
||||
@customElement("ak-expand")
|
||||
export class Expand extends AKElement implements IExpand {
|
||||
@property({ type: Boolean })
|
||||
expanded = false;
|
||||
public expanded = false;
|
||||
|
||||
@property({ type: String, attribute: "text-open" })
|
||||
textOpen = msg("Show less");
|
||||
public textOpen = msg("Show less");
|
||||
|
||||
@property({ type: String, attribute: "text-closed" })
|
||||
textClosed = msg("Show more");
|
||||
public textClosed = msg("Show more");
|
||||
|
||||
static styles = [
|
||||
PFBase,
|
||||
PFExpandableSection,
|
||||
css`
|
||||
.pf-c-expandable-section.pf-m-display-lg {
|
||||
background-color: var(--pf-global--BackgroundColor--100);
|
||||
.pf-c-expandable-section {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.pf-c-expandable-section__toggle {
|
||||
user-select: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
@@ -46,7 +50,8 @@ export class Expand extends AKElement implements IExpand {
|
||||
<button
|
||||
type="button"
|
||||
class="pf-c-expandable-section__toggle"
|
||||
aria-expanded="${this.expanded}"
|
||||
aria-expanded=${this.expanded ? "true" : "false"}
|
||||
aria-controls="expandable-content"
|
||||
@click=${() => {
|
||||
this.expanded = !this.expanded;
|
||||
}}
|
||||
@@ -58,7 +63,11 @@ export class Expand extends AKElement implements IExpand {
|
||||
>${this.expanded ? this.textOpen : this.textClosed}</span
|
||||
>
|
||||
</button>
|
||||
<div class="pf-c-expandable-section__content" ?hidden=${!this.expanded}>
|
||||
<div
|
||||
id="expandable-content"
|
||||
class="pf-c-expandable-section__content"
|
||||
?hidden=${!this.expanded}
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
@@ -48,6 +48,15 @@ import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
const FlowLayoutClasses = {
|
||||
[FlowLayoutEnum.ContentLeft]: "pf-c-login__container",
|
||||
[FlowLayoutEnum.ContentRight]: "pf-c-login__container content-right",
|
||||
[FlowLayoutEnum.SidebarLeft]: "ak-login-container",
|
||||
[FlowLayoutEnum.SidebarRight]: "ak-login-container",
|
||||
[FlowLayoutEnum.Stacked]: "ak-login-container",
|
||||
[FlowLayoutEnum.UnknownDefaultOpenApi]: "ak-login-container",
|
||||
} as const satisfies Record<FlowLayoutEnum, string>;
|
||||
|
||||
@customElement("ak-flow-executor")
|
||||
export class FlowExecutor
|
||||
extends WithCapabilitiesConfig(WithBrandConfig(Interface))
|
||||
@@ -73,7 +82,12 @@ export class FlowExecutor
|
||||
--pf-c-background-image--BackgroundImage--sm: var(--ak-flow-background);
|
||||
--pf-c-background-image--BackgroundImage--sm-2x: var(--ak-flow-background);
|
||||
--pf-c-background-image--BackgroundImage--lg: var(--ak-flow-background);
|
||||
|
||||
@media (max-width: 768px) {
|
||||
background: var(--pf-c-login__main--BackgroundColor) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ak-hidden {
|
||||
display: none;
|
||||
}
|
||||
@@ -85,7 +99,7 @@ export class FlowExecutor
|
||||
}
|
||||
/* layouts */
|
||||
@media (min-height: 60rem) {
|
||||
.pf-c-login.stacked .pf-c-login__main {
|
||||
.pf-c-login[data-layout="stacked"] .pf-c-login__main {
|
||||
margin-top: 13rem;
|
||||
}
|
||||
}
|
||||
@@ -95,33 +109,34 @@ export class FlowExecutor
|
||||
"footer main"
|
||||
". main";
|
||||
}
|
||||
.pf-c-login.sidebar_left {
|
||||
.pf-c-login[data-layout="sidebar_left"] {
|
||||
justify-content: flex-start;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.pf-c-login.sidebar_left .ak-login-container,
|
||||
.pf-c-login.sidebar_right .ak-login-container {
|
||||
height: 100vh;
|
||||
.pf-c-login[data-layout="sidebar_left"] .ak-login-container,
|
||||
.pf-c-login[data-layout="sidebar_right"] .ak-login-container {
|
||||
height: 100%;
|
||||
min-height: 100dvh;
|
||||
background-color: var(--pf-c-login__main--BackgroundColor);
|
||||
padding-left: var(--pf-global--spacer--lg);
|
||||
padding-right: var(--pf-global--spacer--lg);
|
||||
padding-inline: var(--pf-global--spacer--lg);
|
||||
padding-block-end: var(--pf-global--spacer--xs);
|
||||
}
|
||||
.pf-c-login.sidebar_left .pf-c-list,
|
||||
.pf-c-login.sidebar_right .pf-c-list {
|
||||
.pf-c-login[data-layout="sidebar_left"] .pf-c-list,
|
||||
.pf-c-login[data-layout="sidebar_right"] .pf-c-list {
|
||||
color: #000;
|
||||
}
|
||||
.pf-c-login.sidebar_right {
|
||||
.pf-c-login[data-layout="sidebar_right"] {
|
||||
justify-content: flex-end;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
:host([theme="dark"]) .pf-c-login.sidebar_left .ak-login-container,
|
||||
:host([theme="dark"]) .pf-c-login.sidebar_right .ak-login-container {
|
||||
:host([theme="dark"]) .pf-c-login[data-layout="sidebar_left"] .ak-login-container,
|
||||
:host([theme="dark"]) .pf-c-login[data-layout="sidebar_right"] .ak-login-container {
|
||||
background-color: var(--ak-dark-background);
|
||||
}
|
||||
:host([theme="dark"]) .pf-c-login.sidebar_left .pf-c-list,
|
||||
:host([theme="dark"]) .pf-c-login.sidebar_right .pf-c-list {
|
||||
:host([theme="dark"]) .pf-c-login[data-layout="sidebar_left"] .pf-c-list,
|
||||
:host([theme="dark"]) .pf-c-login[data-layout="sidebar_right"] .pf-c-list {
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
.pf-c-brand {
|
||||
@@ -352,26 +367,10 @@ export class FlowExecutor
|
||||
|
||||
//#region Render
|
||||
|
||||
getLayout(): string {
|
||||
const prefilledFlow = globalAK()?.flow?.layout || FlowLayoutEnum.Stacked;
|
||||
if (this.challenge) {
|
||||
return this.challenge?.flowInfo?.layout || prefilledFlow;
|
||||
}
|
||||
return prefilledFlow;
|
||||
}
|
||||
|
||||
getLayoutClass(): string {
|
||||
const layout = this.getLayout();
|
||||
|
||||
switch (layout) {
|
||||
case FlowLayoutEnum.ContentLeft:
|
||||
return "pf-c-login__container";
|
||||
case FlowLayoutEnum.ContentRight:
|
||||
return "pf-c-login__container content-right";
|
||||
case FlowLayoutEnum.Stacked:
|
||||
default:
|
||||
return "ak-login-container";
|
||||
}
|
||||
get layout(): FlowLayoutEnum {
|
||||
return (
|
||||
this.challenge?.flowInfo?.layout || globalAK()?.flow?.layout || FlowLayoutEnum.Stacked
|
||||
);
|
||||
}
|
||||
|
||||
async renderChallenge(): Promise<TemplateResult> {
|
||||
@@ -548,6 +547,7 @@ export class FlowExecutor
|
||||
return import("#flow/FlowInspector").then(
|
||||
() =>
|
||||
html`<ak-flow-inspector
|
||||
id="flow-inspector"
|
||||
class="pf-c-drawer__panel pf-m-width-33"
|
||||
.flowSlug=${this.flowSlug}
|
||||
></ak-flow-inspector>`,
|
||||
@@ -555,18 +555,24 @@ export class FlowExecutor
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html` <ak-locale-context>
|
||||
<div class="pf-c-background-image"></div>
|
||||
<div class="pf-c-page__drawer">
|
||||
<div class="pf-c-drawer ${this.inspectorOpen ? "pf-m-expanded" : "pf-m-collapsed"}">
|
||||
<div class="pf-c-drawer__main">
|
||||
<div class="pf-c-drawer__content">
|
||||
<div class="pf-c-drawer__body">
|
||||
<div class="pf-c-login ${this.getLayout()}">
|
||||
<div class="${this.getLayoutClass()}">
|
||||
const { layout } = this;
|
||||
|
||||
return html`<ak-locale-context>
|
||||
<div class="pf-c-background-image" part="background-image"></div>
|
||||
<div class="pf-c-page__drawer" part="page-drawer">
|
||||
<div
|
||||
class="pf-c-drawer ${this.inspectorOpen ? "pf-m-expanded" : "pf-m-collapsed"}"
|
||||
part="drawer"
|
||||
>
|
||||
<div class="pf-c-drawer__main" part="drawer-main">
|
||||
<div class="pf-c-drawer__content" part="drawer-content">
|
||||
<div class="pf-c-drawer__body" part="drawer-body">
|
||||
<div class="pf-c-login" data-layout=${layout} part="flow">
|
||||
<div class=${FlowLayoutClasses[layout]} part="flow-container">
|
||||
<main
|
||||
class="pf-c-login__main"
|
||||
aria-label=${msg("Authentication form")}
|
||||
part="flow-main"
|
||||
>
|
||||
${this.loading && this.challenge
|
||||
? html`<ak-loading-overlay></ak-loading-overlay>`
|
||||
@@ -583,6 +589,7 @@ export class FlowExecutor
|
||||
${until(this.renderChallenge())}
|
||||
</main>
|
||||
<ak-brand-links
|
||||
part="brand-links"
|
||||
role="contentinfo"
|
||||
aria-label=${msg("Site footer")}
|
||||
class="pf-c-login__footer"
|
||||
@@ -595,10 +602,11 @@ export class FlowExecutor
|
||||
${this.inspectorAvailable && !this.inspectorOpen
|
||||
? html`<button
|
||||
aria-label=${this.inspectorOpen
|
||||
? msg("Close inspector")
|
||||
: msg("Open inspector")}
|
||||
? msg("Close flow inspector")
|
||||
: msg("Open flow inspector")}
|
||||
aria-expanded=${this.inspectorOpen ? "true" : "false"}
|
||||
class="inspector-toggle pf-c-button pf-m-primary"
|
||||
aria-controls="flow-inspector"
|
||||
@click=${() => {
|
||||
this.inspectorOpen = true;
|
||||
}}
|
||||
|
||||
@@ -48,13 +48,17 @@ export class FlowInspector extends AKElement {
|
||||
.pf-c-drawer__body {
|
||||
height: 100dvh;
|
||||
}
|
||||
:host {
|
||||
background-color: var(--pf-c-notification-drawer--BackgroundColor) !important;
|
||||
}
|
||||
|
||||
.pf-c-notification-drawer__body {
|
||||
/* compatibility-mode-fix */
|
||||
|
||||
& {
|
||||
padding-inline: var(--pf-global--spacer--md);
|
||||
padding-block: var(--pf-global--spacer--xs);
|
||||
}
|
||||
|
||||
.pf-l-stack__item:last-child {
|
||||
padding-block-end: var(--pf-global--spacer--md);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ export class FormStatic extends AKElement {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-flow: wrap;
|
||||
gap: var(--pf-global--spacer--sm);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,12 @@ export class FlowCard extends AKElement {
|
||||
padding: 0;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.pf-c-login__main-body {
|
||||
--pf-c-login__main-body--md--PaddingLeft: var(--pf-global--spacer--md);
|
||||
--pf-c-login__main-body--md--PaddingRight: var(--pf-global--spacer--md);
|
||||
}
|
||||
|
||||
.pf-c-login__main-body:last-child {
|
||||
padding-bottom: calc(var(--pf-c-login__main-header--PaddingTop) * 1.2);
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ export class AuthenticatorEmailStage extends BaseStage<
|
||||
placeholder="${msg("Please enter the code you received via email")}"
|
||||
autofocus=""
|
||||
autocomplete="one-time-code"
|
||||
class="pf-c-form-control"
|
||||
class="pf-c-form-control pf-m-monospace"
|
||||
required
|
||||
/>
|
||||
${AKFormErrors({ errors: this.challenge.responseErrors?.code })}
|
||||
|
||||
@@ -108,7 +108,7 @@ export class AuthenticatorSMSStage extends BaseStage<
|
||||
placeholder="${msg("Please enter the code you received via SMS")}"
|
||||
autofocus=""
|
||||
autocomplete="one-time-code"
|
||||
class="pf-c-form-control"
|
||||
class="pf-c-form-control pf-m-monospace"
|
||||
required
|
||||
/>
|
||||
${AKFormErrors({ errors: this.challenge.responseErrors?.code })}
|
||||
|
||||
@@ -51,7 +51,7 @@ export class AuthenticatorValidateStageWebCode extends BaseDeviceStage<
|
||||
autofocus
|
||||
spellcheck="false"
|
||||
autocomplete="one-time-code"
|
||||
class="pf-c-form-control"
|
||||
class="pf-c-form-control pf-m-monospace"
|
||||
value="${PasswordManagerPrefill.totp || ""}"
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -16,7 +16,9 @@ import type { RACLaunchEndpointModal } from "#user/LibraryApplication/RACLaunchE
|
||||
|
||||
import { Application } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { kebabCase } from "change-case";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { css, CSSResult, html, nothing, TemplateResult } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators.js";
|
||||
import { classMap } from "lit/directives/class-map.js";
|
||||
@@ -52,39 +54,53 @@ export class LibraryApplication extends AKElement {
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
}
|
||||
.pf-c-card__header a {
|
||||
|
||||
.launch-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.expander {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.pf-c-card__title {
|
||||
text-align: center;
|
||||
/* This is not ideal as it hard limits us to 2 lines of text for the title
|
||||
of the application. In theory that should be fine for most cases, but ideally
|
||||
we don't do this */
|
||||
height: 48px;
|
||||
|
||||
display: box;
|
||||
display: -webkit-box;
|
||||
line-clamp: 2;
|
||||
-webkit-line-clamp: 2;
|
||||
box-orient: vertical;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
#openRACLaunchModal = () => {
|
||||
this.racEndpointLaunch?.show();
|
||||
};
|
||||
|
||||
renderExpansion(application: Application) {
|
||||
const { me, uiConfig } = rootInterface<UserInterface>();
|
||||
|
||||
return html`<ak-expand textOpen=${msg("Fewer details")} textClosed=${msg("More details")}>
|
||||
<div class="pf-c-content">
|
||||
return html`<ak-expand text-open=${msg("Details")} text-closed=${msg("Details")}>
|
||||
<div class="pf-c-content" part="card-expansion">
|
||||
<small>${application.metaPublisher}</small>
|
||||
</div>
|
||||
${truncateWords(application.metaDescription || "", 10)}
|
||||
<div id="app-description" part="card-description">
|
||||
${truncateWords(application.metaDescription || "", 10)}
|
||||
</div>
|
||||
${uiConfig?.enabledFeatures.applicationEdit && me?.user.isSuperuser
|
||||
? html`
|
||||
<a
|
||||
class="pf-c-button pf-m-control pf-m-small pf-m-block"
|
||||
aria-label=${msg(str`Edit "${application.name}"`)}
|
||||
href="${globalAK().api
|
||||
.base}if/admin/#/core/applications/${application?.slug}"
|
||||
>
|
||||
@@ -99,51 +115,55 @@ export class LibraryApplication extends AKElement {
|
||||
if (!this.application) {
|
||||
return nothing;
|
||||
}
|
||||
if (this.application?.launchUrl === "goauthentik.io://providers/rac/launch") {
|
||||
return html`<div class="pf-c-card__header">
|
||||
<a
|
||||
@click=${() => {
|
||||
this.racEndpointLaunch?.show();
|
||||
}}
|
||||
>
|
||||
<ak-app-icon
|
||||
size=${PFSize.Large}
|
||||
name=${this.application.name}
|
||||
icon=${ifPresent(this.application.metaIcon)}
|
||||
></ak-app-icon>
|
||||
</a>
|
||||
</div>
|
||||
<div class="pf-c-card__title">
|
||||
<a
|
||||
@click=${() => {
|
||||
this.racEndpointLaunch?.show();
|
||||
}}
|
||||
>
|
||||
${this.application.name}
|
||||
</a>
|
||||
</div>
|
||||
<ak-library-rac-endpoint-launch .app=${this.application}>
|
||||
</ak-library-rac-endpoint-launch>`;
|
||||
}
|
||||
return html`<div class="pf-c-card__header">
|
||||
<a
|
||||
href="${ifPresent(this.application.launchUrl ?? "")}"
|
||||
target="${ifPresent(this.application.openInNewTab, "_blank")}"
|
||||
|
||||
if (this.application.launchUrl === "goauthentik.io://providers/rac/launch") {
|
||||
return html`<div
|
||||
part="card-header"
|
||||
class="pf-c-card__header pf-m-pressable"
|
||||
role="button"
|
||||
aria-label=${msg(
|
||||
str`Open Remote Access Control launcher for "${this.application.name}"`,
|
||||
)}
|
||||
@click=${this.#openRACLaunchModal}
|
||||
>
|
||||
<ak-app-icon
|
||||
part="card-header-icon"
|
||||
size=${PFSize.Large}
|
||||
name=${this.application.name}
|
||||
icon=${ifPresent(this.application.metaIcon)}
|
||||
></ak-app-icon>
|
||||
</a>
|
||||
</div>
|
||||
<div class="pf-c-card__title">
|
||||
<a
|
||||
href="${ifPresent(this.application.launchUrl ?? "")}"
|
||||
target="${ifPresent(this.application.openInNewTab, "_blank")}"
|
||||
>${this.application.name}</a
|
||||
</div>
|
||||
<div
|
||||
@click=${this.#openRACLaunchModal}
|
||||
id="app-title"
|
||||
class="pf-c-card__title pf-m-pressable"
|
||||
part="card-title"
|
||||
>
|
||||
</div>`;
|
||||
${this.application.name}
|
||||
</div>
|
||||
<ak-library-rac-endpoint-launch .app=${this.application}>
|
||||
</ak-library-rac-endpoint-launch>`;
|
||||
}
|
||||
|
||||
return html`<a
|
||||
class="launch-wrapper"
|
||||
part="card-header-link"
|
||||
aria-label=${msg(str`Open "${this.application.name}"`)}
|
||||
href=${ifPresent(this.application.launchUrl)}
|
||||
target=${ifPresent(this.application.openInNewTab, "_blank")}
|
||||
>
|
||||
<div class="pf-c-card__header" part="card-header">
|
||||
<ak-app-icon
|
||||
part="card-header-icon"
|
||||
size=${PFSize.Large}
|
||||
name=${this.application.name}
|
||||
icon=${ifPresent(this.application.metaIcon)}
|
||||
></ak-app-icon>
|
||||
</div>
|
||||
<div id="app-title" class="pf-c-card__title" part="card-title">
|
||||
${this.application.name}
|
||||
</div>
|
||||
</a>`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
@@ -158,11 +178,21 @@ export class LibraryApplication extends AKElement {
|
||||
this.application.metaPublisher !== "" ||
|
||||
this.application.metaDescription !== "";
|
||||
|
||||
const classes = { "pf-m-selectable": this.selected, "pf-m-selected": this.selected };
|
||||
const classes = {
|
||||
"pf-m-selectable": this.selected,
|
||||
"pf-m-selected": this.selected,
|
||||
};
|
||||
|
||||
const styles = this.background ? { background: this.background } : {};
|
||||
return html` <div
|
||||
const applicationName = kebabCase(this.application.name);
|
||||
|
||||
return html`<div
|
||||
role="gridcell"
|
||||
class="pf-c-card pf-m-hoverable pf-m-compact ${classMap(classes)}"
|
||||
style=${styleMap(styles)}
|
||||
data-application-name=${ifPresent(applicationName)}
|
||||
aria-labelledby="app-title"
|
||||
aria-describedby="app-description"
|
||||
>
|
||||
${this.renderLaunch()}
|
||||
<div class="expander"></div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { AppGroupEntry, AppGroupList } from "./types.js";
|
||||
import type { AppGroupList } from "./types.js";
|
||||
|
||||
import { LayoutType } from "#common/ui/config";
|
||||
|
||||
@@ -6,6 +6,7 @@ import { AKElement } from "#elements/Base";
|
||||
|
||||
import type { Application } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
@@ -78,11 +79,19 @@ export class LibraryPageApplicationList extends AKElement {
|
||||
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}">
|
||||
return html`<div
|
||||
class="pf-l-grid pf-m-gutter"
|
||||
role="grid"
|
||||
aria-label=${msg("Available applications")}
|
||||
>
|
||||
${this.apps.map(([group, apps], idx) => {
|
||||
return html`<div
|
||||
class="pf-l-grid__item ${groupClass}"
|
||||
role="rowgroup"
|
||||
aria-labelledby="app-group-${idx}"
|
||||
>
|
||||
<div class="pf-c-content app-group-header">
|
||||
<h2>${group}</h2>
|
||||
<h2 id="app-group-${idx}">${group}</h2>
|
||||
</div>
|
||||
<div class="pf-l-grid pf-m-gutter ${groupGrid}">
|
||||
${apps.map((app: Application) => {
|
||||
|
||||
@@ -17,6 +17,7 @@ import { css, html } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
|
||||
|
||||
@@ -40,21 +41,28 @@ export class LibraryPageApplicationSearch extends AKElement {
|
||||
static styles = [
|
||||
PFBase,
|
||||
PFDisplay,
|
||||
PFFormControl,
|
||||
css`
|
||||
input {
|
||||
width: 30ch;
|
||||
box-sizing: border-box;
|
||||
border: 0;
|
||||
border-bottom: 1px solid;
|
||||
border-bottom-color: var(--ak-accent);
|
||||
input[name="application-search"] {
|
||||
background-color: transparent;
|
||||
width: 30ch;
|
||||
font-size: 1.5rem;
|
||||
|
||||
border-inline: none;
|
||||
border-block-start: none;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
--pf-c-form-control--BorderBottomColor: var(--ak-accent);
|
||||
}
|
||||
}
|
||||
input:focus {
|
||||
outline: 0;
|
||||
}
|
||||
:host([theme="dark"]) input {
|
||||
color: var(--ak-dark-foreground) !important;
|
||||
`,
|
||||
// HACK: Fixes Lit Analyzer's outdated parser.
|
||||
(css as typeof css) /*css*/ `
|
||||
input[name="application-search"] {
|
||||
@media not (prefers-contrast: more) {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
@@ -151,13 +159,14 @@ export class LibraryPageApplicationSearch extends AKElement {
|
||||
|
||||
render() {
|
||||
return html`<input
|
||||
name="application-search"
|
||||
@input=${this.onInput}
|
||||
@keydown=${this.onKeyDown}
|
||||
type="text"
|
||||
class="pf-u-display-none pf-u-display-block-on-md"
|
||||
type="search"
|
||||
class="pf-c-form-control pf-u-display-none pf-u-display-block-on-md"
|
||||
autofocus
|
||||
aria-label=${msg("Search for an application by name")}
|
||||
placeholder=${msg("Search...")}
|
||||
aria-label=${msg("Application search")}
|
||||
placeholder=${msg("Search for an application by name...")}
|
||||
value=${ifDefined(this.query)}
|
||||
/>`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user