mirror of
https://github.com/goauthentik/authentik
synced 2026-05-14 19:06:39 +02:00
Compare commits
2 Commits
issuer-gen
...
fix-flow-l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acb323ce33 | ||
|
|
a0fd402d2d |
@@ -18,8 +18,6 @@
|
||||
|
||||
{% include "base/theme.html" %}
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}">
|
||||
|
||||
<style>{{ brand_css }}</style>
|
||||
<script src="{% versioned_script 'dist/poly-%v.js' %}" type="module"></script>
|
||||
<script src="{% versioned_script 'dist/standalone/loading/index-%v.js' %}" type="module"></script>
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
{% load static %}
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/layers/global.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}">
|
||||
|
||||
{% if ui_theme == "dark" %}
|
||||
<meta name="color-scheme" content="dark" />
|
||||
<meta name="theme-color" content="#18191a">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/layers/global.dark.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/theme-dark.css' %}">
|
||||
{% elif ui_theme == "light" %}
|
||||
<meta name="color-scheme" content="light" />
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
@@ -8,4 +15,7 @@
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
|
||||
<meta name="theme-color" content="#18191a" media="(prefers-color-scheme: dark)">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/layers/global.dark.css' %}" media="(prefers-color-scheme: dark)">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/theme-dark.css' %}" media="(prefers-color-scheme: dark)">
|
||||
{% endif %}
|
||||
|
||||
@@ -6,20 +6,16 @@
|
||||
{% block head_before %}
|
||||
<link rel="prefetch" href="{{ request.brand.branding_default_flow_background_url }}" />
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly.min.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/theme-dark.css' %}" media="(prefers-color-scheme: dark)">
|
||||
{% include "base/header_js.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<style>
|
||||
:root {
|
||||
--ak-flow-background: url("{{ request.brand.branding_default_flow_background_url }}");
|
||||
--pf-c-background-image--BackgroundImage: var(--ak-flow-background);
|
||||
--pf-c-background-image--BackgroundImage-2x: var(--ak-flow-background);
|
||||
--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);
|
||||
}
|
||||
:root {
|
||||
--ak-global--background-color: var(--pf-global--BackgroundColor--150);
|
||||
--ak-global--background-image: url("{{ request.brand.branding_default_flow_background_url }}");
|
||||
}
|
||||
|
||||
/* Form with user */
|
||||
.form-control-static {
|
||||
margin-top: var(--pf-global--spacer--sm);
|
||||
@@ -43,41 +39,37 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="pf-c-background-image">
|
||||
</div>
|
||||
<ak-skip-to-content></ak-skip-to-content>
|
||||
<ak-message-container></ak-message-container>
|
||||
<div class="pf-c-login stacked">
|
||||
<div class="ak-login-container">
|
||||
<main class="pf-c-login__main">
|
||||
<div class="pf-c-login__main-header pf-c-brand ak-brand">
|
||||
<img src="{{ brand.branding_logo_url }}" alt="authentik Logo" />
|
||||
</div>
|
||||
<header class="pf-c-login__main-header">
|
||||
<h1 class="pf-c-title pf-m-3xl">
|
||||
{% block card_title %}
|
||||
{% endblock %}
|
||||
</h1>
|
||||
</header>
|
||||
<div class="pf-c-login__main-body">
|
||||
{% block card %}
|
||||
<div class="pf-c-login">
|
||||
<main class="pf-c-login__main">
|
||||
<div part="branding" class="pf-c-login__main-header pf-c-brand">
|
||||
<img part="branding-logo" src="{{ brand.branding_logo_url }}" alt="authentik Logo" />
|
||||
</div>
|
||||
<header class="pf-c-login__main-header">
|
||||
<h1 class="pf-c-title pf-m-3xl">
|
||||
{% block card_title %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</main>
|
||||
<footer class="pf-c-login__footer">
|
||||
<ul class="pf-c-list pf-m-inline">
|
||||
{% for link in footer_links %}
|
||||
<li>
|
||||
<a href="{{ link.href }}">{{ link.name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li>
|
||||
<span>
|
||||
{% trans 'Powered by authentik' %}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</footer>
|
||||
</div>
|
||||
</h1>
|
||||
</header>
|
||||
<div class="pf-c-login__main-body">
|
||||
{% block card %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</main>
|
||||
<footer class="pf-c-login__footer pf-m-dark">
|
||||
<ul class="pf-c-list pf-m-inline">
|
||||
{% for link in footer_links %}
|
||||
<li>
|
||||
<a href="{{ link.href }}">{{ link.name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li>
|
||||
<span>
|
||||
{% trans 'Powered by authentik' %}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</footer>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -20,9 +20,10 @@ window.authentik.flow = {
|
||||
{% block head %}
|
||||
<script src="{% versioned_script 'dist/flow/FlowInterface-%v.js' %}" type="module"></script>
|
||||
<style>
|
||||
:root {
|
||||
--ak-flow-background: url("{{ flow_background_url }}");
|
||||
}
|
||||
:root {
|
||||
--ak-global--background-color: var(--pf-global--BackgroundColor--150);
|
||||
--ak-global--background-image: url("{{ flow_background_url }}");
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -6,54 +6,51 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<title>{{.Title}}</title>
|
||||
<link rel="shortcut icon" type="image/png" href="/outpost.goauthentik.io/static/dist/assets/icons/icon.png">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/outpost.goauthentik.io/static/dist/patternfly.min.css">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/outpost.goauthentik.io/static/dist/layers/global.css">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/outpost.goauthentik.io/static/dist/authentik.css">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/outpost.goauthentik.io/static/layers/global.dark.css" media="(prefers-color-scheme: dark)">
|
||||
<link rel="stylesheet" type="text/css" href="/outpost.goauthentik.io/static/theme-dark.css" media="(prefers-color-scheme: dark)">
|
||||
|
||||
<link rel="prefetch" href="/outpost.goauthentik.io/static/dist/assets/images/flow_background.jpg" />
|
||||
<style>
|
||||
.pf-c-background-image::before {
|
||||
--ak-flow-background: url("/outpost.goauthentik.io/static/dist/assets/images/flow_background.jpg");
|
||||
}
|
||||
:root {
|
||||
--ak-flow-background: url("/outpost.goauthentik.io/static/dist/assets/images/flow_background.jpg");
|
||||
--pf-c-background-image--BackgroundImage: var(--ak-flow-background);
|
||||
--pf-c-background-image--BackgroundImage-2x: var(--ak-flow-background);
|
||||
--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);
|
||||
--ak-global--background-color: var(--pf-global--BackgroundColor--150);
|
||||
--ak-global--background-image: url("/outpost.goauthentik.io/static/dist/assets/images/flow_background.jpg");
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="pf-c-background-image">
|
||||
</div>
|
||||
<div class="pf-c-login stacked">
|
||||
<div class="ak-login-container">
|
||||
<main class="pf-c-login__main">
|
||||
<div class="pf-c-login__main-header pf-c-brand ak-brand">
|
||||
<img src="/outpost.goauthentik.io/static/dist/assets/icons/icon_left_brand.svg" alt="authentik Logo" />
|
||||
</div>
|
||||
<header class="pf-c-login__main-header">
|
||||
<h1 class="pf-c-title pf-m-3xl">
|
||||
{{ .Title }}
|
||||
</h1>
|
||||
</header>
|
||||
<div class="pf-c-login__main-body">
|
||||
{{ .Message }}
|
||||
</div>
|
||||
<div class="pf-c-login__main-body">
|
||||
<a href="/" class="pf-c-button pf-m-primary pf-m-block">Go to home</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="pf-c-login__footer">
|
||||
<ul class="pf-c-list pf-m-inline">
|
||||
<li>
|
||||
<span>
|
||||
Powered by authentik
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="pf-c-login">
|
||||
<main class="pf-c-login__main">
|
||||
<div part="branding" class="pf-c-login__main-header pf-c-brand">
|
||||
<img part="branding-logo" src="/outpost.goauthentik.io/static/dist/assets/icons/icon_left_brand.svg" alt="authentik Logo" />
|
||||
</div>
|
||||
<header class="pf-c-login__main-header">
|
||||
<h1 class="pf-c-title pf-m-3xl">
|
||||
{{ .Title }}
|
||||
</h1>
|
||||
</header>
|
||||
<div class="pf-c-login__main-body">
|
||||
{{ .Message }}
|
||||
</div>
|
||||
<div class="pf-c-login__main-body">
|
||||
<a href="/" class="pf-c-button pf-m-primary pf-m-block">Go to home</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="pf-c-login__footer pf-m-dark">
|
||||
<ul class="pf-c-list pf-m-inline">
|
||||
<li>
|
||||
<span>
|
||||
Powered by authentik
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -99,17 +99,6 @@ export class AdminInterface extends WithCapabilitiesConfig(AuthenticatedInterfac
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pf-c-page {
|
||||
background-color: var(--pf-c-page--BackgroundColor) !important;
|
||||
}
|
||||
|
||||
:host([theme="dark"]) {
|
||||
/* Global page background colour */
|
||||
.pf-c-page {
|
||||
--pf-c-page--BackgroundColor: var(--ak-dark-background);
|
||||
}
|
||||
}
|
||||
|
||||
ak-page-navbar {
|
||||
grid-area: header;
|
||||
}
|
||||
|
||||
@@ -7,43 +7,6 @@
|
||||
--__AK_UI_BASE__: 1;
|
||||
}
|
||||
|
||||
/* #region Global */
|
||||
|
||||
:root {
|
||||
--ak-accent: #fd4b2d;
|
||||
|
||||
--ak-dark-foreground: #fafafa;
|
||||
--ak-dark-foreground-darker: #bebebe;
|
||||
--ak-dark-foreground-link: #5a5cb9;
|
||||
--ak-dark-background: #18191a;
|
||||
--ak-dark-background-darker: #000000;
|
||||
--ak-dark-background-light: #1c1e21;
|
||||
--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);
|
||||
|
||||
/* Minimum width after which the sidebar becomes automatic */
|
||||
--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;
|
||||
|
||||
--pf-global--disabled-color--100: GrayText;
|
||||
--pf-global--disabled-color--200: color-mix(in srgb, GrayText 100%, CanvasText 75%);
|
||||
--pf-global--disabled-color--300: color-mix(in srgb, GrayText 100%, CanvasText 100%);
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Scrollbars */
|
||||
|
||||
/**
|
||||
@@ -63,6 +26,16 @@
|
||||
--ak-scrollbar-thumb-background-color: hsl(0 0% 76%);
|
||||
}
|
||||
|
||||
.pf-m-dark {
|
||||
--pf-global--Color--100: var(--pf-global--Color--dark-100);
|
||||
--pf-global--Color--200: var(--pf-global--Color--dark-200);
|
||||
--pf-global--BorderColor--100: var(--pf-global--BorderColor--dark-100);
|
||||
--pf-global--primary-color--100: var(--pf-global--primary-color--dark-100);
|
||||
--pf-global--link--Color: var(--pf-global--link--Color--dark);
|
||||
--pf-global--link--Color--hover: var(--pf-global--link--Color--dark);
|
||||
--pf-global--BackgroundColor--100: var(--pf-global--BackgroundColor--light-100);
|
||||
}
|
||||
|
||||
/* Applicable to browsers with a WebKit lineage (Chrome, Edge, Safari) */
|
||||
::-webkit-scrollbar {
|
||||
background: var(--ak-scrollbar-background-color);
|
||||
@@ -156,19 +129,6 @@
|
||||
color: var(--pf-c-form__label-required--Color);
|
||||
}
|
||||
|
||||
html {
|
||||
--pf-c-nav__link--PaddingTop: 0.5rem;
|
||||
--pf-c-nav__link--PaddingRight: 0.5rem;
|
||||
--pf-c-nav__link--PaddingBottom: 0.5rem;
|
||||
--pf-c-nav__link--PaddingLeft: 0.5rem;
|
||||
}
|
||||
|
||||
html > form > input {
|
||||
position: absolute;
|
||||
top: -2000px;
|
||||
left: -2000px;
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Screen readers */
|
||||
@@ -334,49 +294,222 @@ ak-tabs[vertical] {
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Login adjustments */
|
||||
/* #region Login */
|
||||
|
||||
/* compatibility-mode-fix */
|
||||
.pf-c-login.pf-c-login {
|
||||
--ak-login--header-min-height: var(--pf-global--spacer--lg);
|
||||
--ak-login--header-max-height: clamp(0, 45%, 15dvh);
|
||||
--ak-login--max-width: 35rem;
|
||||
--ak-login--main-column-width: minmax(min-content, var(--ak-login--max-width));
|
||||
--pf-c-login__main-footer--PaddingBottom: 0;
|
||||
|
||||
--pf-c-login__main-body--PaddingBottom: clamp(
|
||||
var(--pf-global--spacer--xs),
|
||||
7dvw,
|
||||
var(--pf-global--spacer--3xl)
|
||||
);
|
||||
|
||||
/* Ensure card is displayed on small screens */
|
||||
.pf-c-login__main {
|
||||
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;
|
||||
padding: 0;
|
||||
|
||||
display: grid;
|
||||
justify-content: space-between;
|
||||
|
||||
grid-template-rows:
|
||||
[header] minmax(0, clamp(1rem, 15%, 15dvh))
|
||||
[main] minmax(auto, min-content)
|
||||
[footer] auto;
|
||||
|
||||
grid-template-columns:
|
||||
1fr
|
||||
[main] var(--ak-login--main-column-width)
|
||||
1fr;
|
||||
|
||||
grid-template-areas:
|
||||
"header header header"
|
||||
" . main . "
|
||||
"footer footer footer";
|
||||
|
||||
&::before {
|
||||
display: block;
|
||||
content: "";
|
||||
background-color: var(--ak-login--background-color-overlay, transparent);
|
||||
z-index: -1;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&::before,
|
||||
[part="login-overlay"] {
|
||||
grid-row: header / footer;
|
||||
grid-column: header;
|
||||
}
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
content: "";
|
||||
grid-area: main;
|
||||
z-index: -1;
|
||||
height: 75dvh;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ak-login-container {
|
||||
max-width: 35rem;
|
||||
width: 100%;
|
||||
/* #region Login Main */
|
||||
|
||||
.pf-c-login__main {
|
||||
--pf-c-login__container--PaddingLeft: 0 !important;
|
||||
--pf-c-login__container--PaddingRight: 0 !important;
|
||||
|
||||
aspect-ratio: 1.2/ 1;
|
||||
justify-content: space-between;
|
||||
grid-area: main;
|
||||
margin: 0;
|
||||
|
||||
--ak-login--padding-max: 8dvw;
|
||||
--ak-login--padding: clamp(
|
||||
var(--pf-global--spacer--md),
|
||||
var(--pf-global--spacer--2xl),
|
||||
var(--ak-login--padding-max)
|
||||
);
|
||||
|
||||
position: relative;
|
||||
max-width: var(--ak-login--max-width);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh - var(--pf-global--spacer--lg) - var(--pf-global--spacer--lg));
|
||||
flex-flow: column;
|
||||
|
||||
.slotted-content {
|
||||
position: relative;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-login__main-header {
|
||||
padding-inline: var(--ak-login--padding);
|
||||
padding-block: clamp(var(--pf-global--spacer--xs), 6dvw, var(--pf-global--spacer--lg));
|
||||
|
||||
.pf-c-title {
|
||||
font-size: clamp(1rem, var(--pf-c-title--m-3xl--FontSize), 7dvw);
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-login__main-header.pf-c-brand {
|
||||
padding-inline: calc(var(--ak-login--padding) / 4);
|
||||
padding-block-start: clamp(var(--pf-global--spacer--xs), 7dvw, var(--pf-global--spacer--3xl));
|
||||
padding-block-end: calc(var(--ak-login--padding) / 2);
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
[part="branding-logo"] {
|
||||
display: block;
|
||||
width: clamp(75%, calc(var(--ak-login--max-width) / 2), 90%);
|
||||
min-height: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-login__main-body {
|
||||
padding-inline: var(--ak-login--padding);
|
||||
}
|
||||
|
||||
.pf-c-login__main-footer {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pf-c-login__main-footer-band {
|
||||
@media (max-width: 35rem) or (max-height: 17.5rem) {
|
||||
--pf-c-login__main-footer-band--BackgroundColor: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 70rem) and (min-height: 17.5rem) {
|
||||
.pf-c-login[data-layout="content_left"],
|
||||
.pf-c-login[data-layout="content_right"] {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
|
||||
place-content: space-between;
|
||||
gap: var(--pf-global--spacer--lg);
|
||||
|
||||
.pf-c-login__main,
|
||||
.pf-c-login__footer {
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-login[data-layout="content_right"] {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.pf-c-login[data-layout="sidebar_left"],
|
||||
.pf-c-login[data-layout="sidebar_right"] {
|
||||
--ak-login--max-width: 36rem;
|
||||
--ak-login--background-color-overlay: var(--pf-c-login__main--BackgroundColor);
|
||||
.pf-c-login__main {
|
||||
aspect-ratio: auto;
|
||||
|
||||
height: 100%;
|
||||
justify-content: normal;
|
||||
}
|
||||
|
||||
.pf-c-login__footer {
|
||||
color: inherit;
|
||||
flex: 1 1 auto;
|
||||
justify-content: end;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-login[data-layout="sidebar_left"] {
|
||||
grid-template-columns: [main footer] var(--ak-login--main-column-width) repeat(2, 1fr);
|
||||
|
||||
grid-template-areas:
|
||||
"header . ."
|
||||
"main . ."
|
||||
"footer . .";
|
||||
}
|
||||
|
||||
.pf-c-login[data-layout="sidebar_right"] {
|
||||
grid-template-columns: repeat(2, 1fr) var(--ak-login--main-column-width) [main footer];
|
||||
|
||||
grid-template-areas:
|
||||
". . header"
|
||||
". . main "
|
||||
". . footer";
|
||||
}
|
||||
|
||||
.pf-c-login__main-footer-band {
|
||||
--pf-c-login__main-footer-band--BackgroundColor: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
.pf-c-data-list {
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
|
||||
.pf-c-login__footer {
|
||||
color: var(--ak-flow-footer-color);
|
||||
flex: 250 0 auto;
|
||||
--pf-global--Color--100: var(--pf-global--Color--light-100);
|
||||
min-height: var(--pf-global--spacer--2xl);
|
||||
grid-area: footer;
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
flex-direction: column;
|
||||
}
|
||||
align-self: end;
|
||||
padding-inline: var(--pf-global--spacer--sm);
|
||||
padding-block: var(--pf-global--spacer--md) !important;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
:root {
|
||||
--ak-flow-footer-color: var(--ak-flow-background-color-contrast);
|
||||
ul.pf-c-list.pf-m-inline {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-login__footer ul.pf-c-list.pf-m-inline {
|
||||
justify-content: center;
|
||||
padding: 2rem 0;
|
||||
@media (max-width: 35rem) {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
@@ -394,12 +527,6 @@ ak-tabs[vertical] {
|
||||
margin-right: var(--pf-global--spacer--sm);
|
||||
}
|
||||
|
||||
/* ensure background on non-flow pages match */
|
||||
.pf-c-background-image::before {
|
||||
background-image: var(--ak-flow-background);
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.pf-m-success {
|
||||
color: var(--pf-global--success-color--100) !important;
|
||||
}
|
||||
@@ -448,6 +575,11 @@ fieldset {
|
||||
|
||||
&:has(legend.sr-only) {
|
||||
border-width: 0;
|
||||
|
||||
&:not(.pf-c-modal-box__footer) {
|
||||
--ak-legend-padding-inline-base: 0;
|
||||
--ak-legend-margin-inline-base: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.pf-c-form__group {
|
||||
@@ -464,6 +596,12 @@ fieldset {
|
||||
}
|
||||
}
|
||||
|
||||
&.pf-c-login__main-footer-band {
|
||||
& > *:last-child {
|
||||
padding-block-end: var(--pf-c-login__main-footer-band-item--PaddingTop);
|
||||
}
|
||||
}
|
||||
|
||||
&.pf-c-modal-box__footer {
|
||||
--ak-legend-padding-inline-base: var(--pf-global--spacer--md);
|
||||
padding-block: calc(var(--ak-legend-padding-inline-base) / 2);
|
||||
@@ -571,42 +709,6 @@ fieldset {
|
||||
}
|
||||
}
|
||||
|
||||
/* Flow-card adjustments for static pages */
|
||||
.pf-c-brand {
|
||||
padding-top: calc(
|
||||
var(--pf-c-login__main-footer-links--PaddingTop) +
|
||||
var(--pf-c-login__main-footer-links--PaddingBottom) +
|
||||
var(--pf-c-login__main-body--PaddingBottom)
|
||||
);
|
||||
max-height: 9rem;
|
||||
}
|
||||
|
||||
.ak-brand {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ak-brand img {
|
||||
padding: 0 2rem;
|
||||
max-height: inherit;
|
||||
}
|
||||
|
||||
@media (min-height: 60rem) {
|
||||
.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;
|
||||
}
|
||||
|
||||
/* #region Code blocks */
|
||||
|
||||
pre:has(.hljs) {
|
||||
|
||||
83
web/src/common/styles/layers/global.css
Normal file
83
web/src/common/styles/layers/global.css
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* @file authentik global layer.
|
||||
*/
|
||||
|
||||
/* #region Root */
|
||||
|
||||
:root {
|
||||
--ak-accent: #fd4b2d;
|
||||
|
||||
--ak-dark-foreground: #fafafa;
|
||||
--ak-dark-foreground-darker: #bebebe;
|
||||
--ak-dark-foreground-link: #5a5cb9;
|
||||
--ak-dark-background: #18191a;
|
||||
--ak-dark-background-darker: #000000;
|
||||
--ak-dark-background-light: #1c1e21;
|
||||
--ak-dark-background-light-ish: #212427;
|
||||
--ak-dark-background-lighter: #2b2e33;
|
||||
|
||||
--ak-global--background-contrast: var(--pf-global--Color--100);
|
||||
|
||||
/* PatternFly likes to override global variables for some reason */
|
||||
--ak-global--Color--100: var(--pf-global--Color--100);
|
||||
|
||||
/* Minimum width after which the sidebar becomes automatic */
|
||||
--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;
|
||||
|
||||
--pf-global--disabled-color--100: GrayText;
|
||||
--pf-global--disabled-color--200: color-mix(in srgb, GrayText 100%, CanvasText 75%);
|
||||
--pf-global--disabled-color--300: color-mix(in srgb, GrayText 100%, CanvasText 100%);
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Document */
|
||||
|
||||
html,
|
||||
body {
|
||||
height: auto;
|
||||
min-height: 100dvh;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--ak-global--background-color, var(--pf-global--BackgroundColor--150));
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-attachment: local;
|
||||
background-image: none;
|
||||
|
||||
@media (min-width: 35rem) and (min-height: 17.5rem) {
|
||||
background-image: var(--ak-global--background-image, none);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
html {
|
||||
--pf-c-nav__link--PaddingTop: 0.5rem;
|
||||
--pf-c-nav__link--PaddingRight: 0.5rem;
|
||||
--pf-c-nav__link--PaddingBottom: 0.5rem;
|
||||
--pf-c-nav__link--PaddingLeft: 0.5rem;
|
||||
}
|
||||
|
||||
html > form > input {
|
||||
position: absolute;
|
||||
top: -2000px;
|
||||
left: -2000px;
|
||||
}
|
||||
29
web/src/common/styles/layers/global.dark.css
Normal file
29
web/src/common/styles/layers/global.dark.css
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* @file authentik global dark layer.
|
||||
*/
|
||||
|
||||
/* #region Global */
|
||||
|
||||
:root {
|
||||
/* TODO: We've seemed to have drifted from PF's dark-100 usage. Revisit this after PF 5. */
|
||||
--pf-global--BackgroundColor--100: #1c1e21 !important;
|
||||
--pf-global--BackgroundColor--dark-100: #18191a !important;
|
||||
--pf-global--BackgroundColor--150: var(--pf-global--BackgroundColor--dark-100) !important;
|
||||
--pf-global--BackgroundColor--200: var(--pf-global--BackgroundColor--dark-200) !important;
|
||||
--pf-global--BackgroundColor--300: var(--pf-global--BackgroundColor--dark-300) !important;
|
||||
--pf-global--Color--100: var(--ak-dark-foreground) !important;
|
||||
--ak-global--Color--100: var(--ak-dark-foreground) !important;
|
||||
--pf-global--BorderColor--100: var(--ak-dark-background-lighter) !important;
|
||||
--pf-global--link--Color: var(--pf-global--link--Color--light) !important;
|
||||
--pf-global--link--Color--hover: var(--pf-global--link--Color--light--hover) !important;
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Document */
|
||||
|
||||
body {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
@@ -7,22 +7,6 @@
|
||||
--__AK_UI_DARK__: 1;
|
||||
}
|
||||
|
||||
/* #region Global */
|
||||
|
||||
:root {
|
||||
--pf-global--Color--100: var(--ak-dark-foreground) !important;
|
||||
--ak-global--Color--100: var(--ak-dark-foreground) !important;
|
||||
--pf-c-page__main-section--m-light--BackgroundColor: var(--ak-dark-background-darker);
|
||||
--pf-global--BorderColor--100: var(--ak-dark-background-lighter) !important;
|
||||
--pf-global--link--Color: var(--pf-global--link--Color--light);
|
||||
--pf-global--link--Color--hover: var(--pf-global--link--Color--light--hover);
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--ak-dark-background) !important;
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
/* #region Scrollbars */
|
||||
|
||||
:root {
|
||||
@@ -33,6 +17,27 @@ body {
|
||||
--ak-scrollbar-thumb-background-color: hsl(0 0% 42%);
|
||||
}
|
||||
|
||||
body:has(ak-flow-executor) {
|
||||
background-color: var(--pf-global--BackgroundColor--150);
|
||||
}
|
||||
|
||||
/**
|
||||
* Our reversal of Patternfly's light variables requires us to set the colors
|
||||
* back to their defaults when in dark mode.
|
||||
*/
|
||||
.pf-m-dark {
|
||||
--pf-global--Color--100: var(--pf-global--Color--light-100);
|
||||
--pf-global--Color--200: var(--pf-global--Color--light-200);
|
||||
--pf-global--BorderColor--100: var(--pf-global--BorderColor--light-100);
|
||||
--pf-global--primary-color--100: var(--pf-global--primary-color--light-100);
|
||||
--pf-global--link--Color: var(--pf-global--link--Color--light);
|
||||
--pf-global--link--Color--hover: var(--pf-global--link--Color--light);
|
||||
--pf-global--BackgroundColor--100: var(--pf-global--BackgroundColor--dark-100);
|
||||
}
|
||||
|
||||
.pf-c-login {
|
||||
--pf-c-login__main--BackgroundColor: var(--pf-global--BackgroundColor--dark-100) !important;
|
||||
}
|
||||
/* #endregion */
|
||||
|
||||
.pf-c-radio {
|
||||
@@ -41,15 +46,7 @@ body {
|
||||
|
||||
/* Global page background colour */
|
||||
.pf-c-page {
|
||||
--pf-c-page--BackgroundColor: var(--ak-dark-background);
|
||||
}
|
||||
|
||||
.pf-c-drawer__content {
|
||||
--pf-c-drawer__content--BackgroundColor: var(--ak-dark-background);
|
||||
}
|
||||
|
||||
.pf-c-title {
|
||||
color: var(--ak-dark-foreground);
|
||||
--pf-c-page--BackgroundColor: var(--pf-global--BackgroundColor--dark-100);
|
||||
}
|
||||
|
||||
.pf-u-mb-xl {
|
||||
@@ -61,7 +58,7 @@ body {
|
||||
/* Header sections */
|
||||
|
||||
.pf-c-page__main-section {
|
||||
--pf-c-page__main-section--BackgroundColor: var(--ak-dark-background);
|
||||
--pf-c-page__main-section--BackgroundColor: var(--pf-global--BackgroundColor--dark-100);
|
||||
}
|
||||
|
||||
.sidebar-trigger,
|
||||
@@ -69,26 +66,12 @@ body {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.pf-c-content {
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
|
||||
/* #region Card */
|
||||
|
||||
.pf-c-card {
|
||||
--pf-c-card--BackgroundColor: var(--ak-dark-background-light);
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
|
||||
.pf-c-card.pf-m-non-selectable-raised {
|
||||
--pf-c-card--BackgroundColor: var(--ak-dark-background-lighter);
|
||||
}
|
||||
|
||||
.pf-c-card__title,
|
||||
.pf-c-card__body {
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Fields */
|
||||
@@ -157,8 +140,7 @@ fieldset {
|
||||
/* #region Page layout */
|
||||
|
||||
/**
|
||||
* Our reversal of the page header on the light theme requires us to set the colors
|
||||
* back to their PatternFly defaults.
|
||||
* cf. Color reversal.
|
||||
*/
|
||||
.pf-c-page__header {
|
||||
--pf-global--Color--100: var(--pf-global--Color--light-100);
|
||||
@@ -369,24 +351,15 @@ select.pf-c-form-control {
|
||||
|
||||
/* #region Flows */
|
||||
|
||||
.pf-c-login__main {
|
||||
--pf-c-login__main--BackgroundColor: var(--ak-dark-background);
|
||||
}
|
||||
|
||||
.pf-c-login__main-body,
|
||||
.pf-c-login__main-header,
|
||||
.pf-c-login__main-header-desc {
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
|
||||
.pf-c-login__main-footer-links-item img,
|
||||
.pf-c-login__main-footer-links-item .fas {
|
||||
filter: invert(1);
|
||||
@media (min-width: 35rem) and (min-height: 50rem) {
|
||||
.pf-c-login__main-footer-links-item img,
|
||||
.pf-c-login__main-footer-links-item .fas {
|
||||
filter: invert(1);
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-login__main-footer-band {
|
||||
--pf-c-login__main-footer-band--BackgroundColor: var(--ak-dark-background-lighter);
|
||||
color: var(--ak-dark-foreground);
|
||||
--pf-c-login__main-footer-band--BackgroundColor: var(--pf-global--BackgroundColor--dark-300);
|
||||
}
|
||||
|
||||
.form-control-static {
|
||||
|
||||
@@ -286,6 +286,44 @@ export function applyDocumentTheme(hint: CSSColorSchemeValue | UIThemeHint = "au
|
||||
applyStyleSheets(preferredColorScheme);
|
||||
}
|
||||
|
||||
/**
|
||||
* A CSS variable representing the global background image.
|
||||
*/
|
||||
export const AKBackgroundImageProperty = "--ak-global--background-image";
|
||||
|
||||
/**
|
||||
* Applies the given background image URL to the document body.
|
||||
*
|
||||
* This method is very defensive to avoid unnecessary DOM repaints.
|
||||
*/
|
||||
export function applyBackgroundImageProperty(value?: string | null): void {
|
||||
const fallbackOrigin = window.location.origin;
|
||||
|
||||
if (!value || !URL.canParse(value, fallbackOrigin)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextBackgroundURL = new URL(value, fallbackOrigin);
|
||||
|
||||
const currentBackgroundImage = getComputedStyle(document.body, "::before").backgroundImage;
|
||||
let currentBackgroundImageURL: URL | null = null;
|
||||
|
||||
if (currentBackgroundImage && currentBackgroundImage !== "none") {
|
||||
// Extract URL from background-image property
|
||||
const [, urlMatch] = currentBackgroundImage.match(/url\(["']?([^"']*)["']?\)/) || [];
|
||||
|
||||
if (URL.canParse(urlMatch)) {
|
||||
currentBackgroundImageURL = new URL(urlMatch, fallbackOrigin);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentBackgroundImageURL && currentBackgroundImageURL.href === nextBackgroundURL.href) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.body.style.setProperty(AKBackgroundImageProperty, `url("${nextBackgroundURL.href}")`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root interface element of the page.
|
||||
*
|
||||
|
||||
@@ -8,7 +8,6 @@ import { EVENT_LOCALE_CHANGE, EVENT_LOCALE_REQUEST } from "#common/constants";
|
||||
import { AKElement } from "#elements/Base";
|
||||
import { customEvent } from "#elements/utils/customEvents";
|
||||
|
||||
import { html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
/**
|
||||
@@ -26,6 +25,10 @@ import { customElement, property } from "lit/decorators.js";
|
||||
*/
|
||||
@customElement("ak-locale-context")
|
||||
export class LocaleContext extends WithBrandConfig(AKElement) {
|
||||
protected createRenderRoot(): HTMLElement | DocumentFragment {
|
||||
return this;
|
||||
}
|
||||
|
||||
/// @attribute The text representation of the current locale */
|
||||
@property({ attribute: true, type: String })
|
||||
locale = DEFAULT_LOCALE;
|
||||
@@ -90,10 +93,6 @@ export class LocaleContext extends WithBrandConfig(AKElement) {
|
||||
// works just fine for almost every use case.
|
||||
this.dispatchEvent(customEvent(EVENT_LOCALE_CHANGE));
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<slot></slot>`;
|
||||
}
|
||||
}
|
||||
|
||||
export default LocaleContext;
|
||||
|
||||
20
web/src/flow/FlowExecutor.css
Normal file
20
web/src/flow/FlowExecutor.css
Normal file
@@ -0,0 +1,20 @@
|
||||
:host {
|
||||
--pf-c-login__main-body--PaddingBottom: var(--pf-global--spacer--2xl);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pf-c-drawer__body {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
.pf-c-drawer__content {
|
||||
--pf-c-drawer__content--BackgroundColor: transparent;
|
||||
}
|
||||
|
||||
.inspector-toggle {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 100;
|
||||
}
|
||||
@@ -9,11 +9,14 @@ import "#flow/stages/FlowErrorStage";
|
||||
import "#flow/stages/FlowFrameStage";
|
||||
import "#flow/stages/RedirectStage";
|
||||
|
||||
import Styles from "./FlowExecutor.css";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import { EVENT_FLOW_ADVANCE, EVENT_FLOW_INSPECTOR_TOGGLE } from "#common/constants";
|
||||
import { pluckErrorDetail } from "#common/errors/network";
|
||||
import { globalAK } from "#common/global";
|
||||
import { configureSentry } from "#common/sentry/index";
|
||||
import { applyBackgroundImageProperty } from "#common/theme";
|
||||
import { WebsocketClient } from "#common/ws";
|
||||
|
||||
import { Interface } from "#elements/Interface";
|
||||
@@ -35,7 +38,7 @@ import {
|
||||
} from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { css, CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit";
|
||||
import { CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||
import { until } from "lit/directives/until.js";
|
||||
@@ -48,20 +51,14 @@ 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))
|
||||
implements StageHost
|
||||
{
|
||||
static readonly DefaultLayout: FlowLayoutEnum =
|
||||
globalAK()?.flow?.layout || FlowLayoutEnum.Stacked;
|
||||
|
||||
//#region Styles
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
@@ -72,99 +69,7 @@ export class FlowExecutor
|
||||
PFTitle,
|
||||
PFList,
|
||||
PFBackgroundImage,
|
||||
css`
|
||||
:host {
|
||||
--pf-c-login__main-body--PaddingBottom: var(--pf-global--spacer--2xl);
|
||||
}
|
||||
.pf-c-background-image::before {
|
||||
--pf-c-background-image--BackgroundImage: var(--ak-flow-background);
|
||||
--pf-c-background-image--BackgroundImage-2x: var(--ak-flow-background);
|
||||
--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;
|
||||
}
|
||||
:host {
|
||||
position: relative;
|
||||
}
|
||||
.pf-c-drawer__content {
|
||||
background-color: transparent;
|
||||
}
|
||||
.pf-c-login {
|
||||
align-items: baseline;
|
||||
}
|
||||
/* layouts */
|
||||
@media (min-height: 60rem) {
|
||||
.pf-c-login[data-layout="stacked"] .pf-c-login__main {
|
||||
margin-top: 13rem;
|
||||
}
|
||||
}
|
||||
.pf-c-login__container.content-right {
|
||||
grid-template-areas:
|
||||
"header main"
|
||||
"footer main"
|
||||
". main";
|
||||
}
|
||||
.pf-c-login[data-layout="sidebar_left"] {
|
||||
justify-content: flex-start;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.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-inline: var(--pf-global--spacer--lg);
|
||||
padding-block-end: var(--pf-global--spacer--xs);
|
||||
}
|
||||
.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[data-layout="sidebar_right"] {
|
||||
justify-content: flex-end;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
: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[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 {
|
||||
padding-top: calc(
|
||||
var(--pf-c-login__main-footer-links--PaddingTop) +
|
||||
var(--pf-c-login__main-footer-links--PaddingBottom) +
|
||||
var(--pf-c-login__main-body--PaddingBottom)
|
||||
);
|
||||
max-height: 9rem;
|
||||
}
|
||||
.ak-brand {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.ak-brand img {
|
||||
padding: 0 2rem;
|
||||
max-height: inherit;
|
||||
}
|
||||
.inspector-toggle {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 100;
|
||||
}
|
||||
`,
|
||||
Styles,
|
||||
];
|
||||
|
||||
//#endregion
|
||||
@@ -204,6 +109,9 @@ export class FlowExecutor
|
||||
@state()
|
||||
protected inspectorAvailable?: boolean;
|
||||
|
||||
@state()
|
||||
protected layout: FlowLayoutEnum = FlowExecutor.DefaultLayout;
|
||||
|
||||
@state()
|
||||
public flowInfo?: ContextualFlowInfo;
|
||||
|
||||
@@ -297,8 +205,14 @@ export class FlowExecutor
|
||||
|
||||
// DOM post-processing has to happen after the render.
|
||||
public updated(changedProperties: PropertyValues<this>) {
|
||||
super.updated(changedProperties);
|
||||
|
||||
if (changedProperties.has("challenge") && this.challenge?.flowInfo) {
|
||||
this.layout = this.challenge?.flowInfo?.layout || FlowExecutor.DefaultLayout;
|
||||
}
|
||||
|
||||
if (changedProperties.has("flowInfo") && this.flowInfo) {
|
||||
this.#setShadowStyles(this.flowInfo);
|
||||
applyBackgroundImageProperty(this.flowInfo.background);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,29 +272,10 @@ export class FlowExecutor
|
||||
});
|
||||
};
|
||||
|
||||
#setShadowStyles(value: ContextualFlowInfo) {
|
||||
if (!value) return;
|
||||
|
||||
this.shadowRoot
|
||||
?.querySelectorAll<HTMLDivElement>(".pf-c-background-image")
|
||||
.forEach((bg) => {
|
||||
bg.style.setProperty("--ak-flow-background", `url('${value?.background}')`);
|
||||
});
|
||||
}
|
||||
|
||||
//#region Render
|
||||
|
||||
get layout(): FlowLayoutEnum {
|
||||
return (
|
||||
this.challenge?.flowInfo?.layout || globalAK()?.flow?.layout || FlowLayoutEnum.Stacked
|
||||
);
|
||||
}
|
||||
|
||||
async renderChallenge(): Promise<TemplateResult> {
|
||||
if (!this.challenge) {
|
||||
return html`<ak-flow-card loading></ak-flow-card>`;
|
||||
}
|
||||
switch (this.challenge?.component) {
|
||||
async renderChallenge(component: ChallengeTypes["component"]): Promise<TemplateResult> {
|
||||
switch (component) {
|
||||
case "ak-stage-access-denied":
|
||||
await import("#flow/stages/access_denied/AccessDeniedStage");
|
||||
return html`<ak-stage-access-denied
|
||||
@@ -569,11 +464,17 @@ export class FlowExecutor
|
||||
);
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
protected renderLoading(): TemplateResult {
|
||||
return html`<div class="slotted-content">
|
||||
<slot></slot>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
public override render(): TemplateResult {
|
||||
const { layout } = this;
|
||||
const { component } = this.challenge || {};
|
||||
|
||||
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"}"
|
||||
@@ -582,35 +483,44 @@ export class FlowExecutor
|
||||
<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"
|
||||
<div class="pf-c-login" data-layout=${layout} part="login">
|
||||
${this.loading && this.challenge
|
||||
? html`<ak-loading-overlay
|
||||
part="login-overlay"
|
||||
></ak-loading-overlay>`
|
||||
: nothing}
|
||||
|
||||
<main
|
||||
class="pf-c-login__main"
|
||||
aria-label=${msg("Authentication form")}
|
||||
part="flow-main"
|
||||
>
|
||||
<div
|
||||
class="pf-c-login__main-header pf-c-brand"
|
||||
part="branding"
|
||||
>
|
||||
${this.loading && this.challenge
|
||||
? html`<ak-loading-overlay></ak-loading-overlay>`
|
||||
: nothing}
|
||||
<div
|
||||
class="pf-c-login__main-header pf-c-brand ak-brand"
|
||||
>
|
||||
<img
|
||||
src="${themeImage(this.brandingLogo)}"
|
||||
alt="${msg("authentik Logo")}"
|
||||
role="presentation"
|
||||
/>
|
||||
</div>
|
||||
${until(this.renderChallenge())}
|
||||
</main>
|
||||
<ak-brand-links
|
||||
part="brand-links"
|
||||
role="contentinfo"
|
||||
aria-label=${msg("Site footer")}
|
||||
class="pf-c-login__footer"
|
||||
.links=${this.brandingFooterLinks}
|
||||
></ak-brand-links>
|
||||
</div>
|
||||
<img
|
||||
part="branding-logo"
|
||||
src="${themeImage(this.brandingLogo)}"
|
||||
alt="${msg("authentik Logo")}"
|
||||
role="presentation"
|
||||
/>
|
||||
</div>
|
||||
${component
|
||||
? until(this.renderChallenge(component))
|
||||
: this.renderLoading()}
|
||||
</main>
|
||||
|
||||
<ak-brand-links
|
||||
part="brand-links"
|
||||
role="contentinfo"
|
||||
aria-label=${msg("Site footer")}
|
||||
class="pf-c-login__footer ${layout ===
|
||||
FlowLayoutEnum.Stacked
|
||||
? "pf-m-dark"
|
||||
: ""}"
|
||||
.links=${this.brandingFooterLinks}
|
||||
></ak-brand-links>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
height: 100dvh;
|
||||
}
|
||||
|
||||
.pf-c-card {
|
||||
--pf-c-card--BackgroundColor: var(--pf-c-notification-drawer--BackgroundColor);
|
||||
}
|
||||
|
||||
:host {
|
||||
background-color: var(--pf-c-notification-drawer--BackgroundColor);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import "#elements/EmptyState";
|
||||
|
||||
import { AKElement } from "#elements/Base";
|
||||
import { SlottedTemplateResult } from "#elements/types";
|
||||
|
||||
import { ChallengeTypes } from "@goauthentik/api";
|
||||
|
||||
import { css, CSSResult, html, nothing } from "lit";
|
||||
import { CSSResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
@@ -30,32 +31,7 @@ export class FlowCard extends AKElement {
|
||||
@property({ type: Boolean })
|
||||
loading = false;
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
PFBase,
|
||||
PFLogin,
|
||||
PFTitle,
|
||||
css`
|
||||
.pf-c-login__main-footer {
|
||||
display: block;
|
||||
}
|
||||
|
||||
slot[name="footer-band"] {
|
||||
text-align: center;
|
||||
background-color: var(--pf-c-login__main-footer-band--BackgroundColor);
|
||||
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);
|
||||
}
|
||||
`,
|
||||
];
|
||||
static styles: CSSResult[] = [PFBase, PFLogin, PFTitle];
|
||||
|
||||
render() {
|
||||
let inner = html`<slot></slot>`;
|
||||
@@ -63,25 +39,22 @@ export class FlowCard extends AKElement {
|
||||
inner = html`<ak-empty-state loading default-label></ak-empty-state>`;
|
||||
}
|
||||
// No title if the challenge doesn't provide a title and no custom title is set
|
||||
let title = undefined;
|
||||
let title: null | SlottedTemplateResult = null;
|
||||
if (this.hasSlotted("title")) {
|
||||
title = html`<h1 class="pf-c-title pf-m-3xl"><slot name="title"></slot></h1>`;
|
||||
} else if (this.challenge?.flowInfo?.title) {
|
||||
title = html`<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo.title}</h1>`;
|
||||
}
|
||||
return html`${title ? html`<div class="pf-c-login__main-header">${title}</div>` : nothing}
|
||||
const footer = this.hasSlotted("footer") ? html`<slot name="footer"></slot>` : null;
|
||||
const footerBand = this.hasSlotted("footer-band")
|
||||
? html`<slot name="footer-band"></slot>`
|
||||
: null;
|
||||
|
||||
return html`${title ? html`<div class="pf-c-login__main-header">${title}</div>` : null}
|
||||
<div class="pf-c-login__main-body">${inner}</div>
|
||||
${this.hasSlotted("footer") || this.hasSlotted("footer-band")
|
||||
? html`<footer class="pf-c-login__main-footer">
|
||||
${this.hasSlotted("footer") ? html`<slot name="footer"></slot>` : nothing}
|
||||
${this.hasSlotted("footer-band")
|
||||
? html`<slot
|
||||
name="footer-band"
|
||||
class="pf-c-login__main-footer-band"
|
||||
></slot>`
|
||||
: nothing}
|
||||
</footer>`
|
||||
: nothing}`;
|
||||
${footer || footerBand
|
||||
? html`<div class="pf-c-login__main-footer">${footer}${footerBand}</div>`
|
||||
: null}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
.authenticator-button {
|
||||
/* compatibility-mode-fix */
|
||||
& {
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(auto, 2rem) minmax(33%, max-content);
|
||||
gap: var(--pf-global--spacer--lg);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--pf-global--BackgroundColor--200);
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: var(--pf-global--icon--FontSize--lg);
|
||||
}
|
||||
|
||||
.content {
|
||||
text-align: left;
|
||||
}
|
||||
@@ -3,6 +3,8 @@ import "#flow/stages/authenticator_validate/AuthenticatorValidateStageCode";
|
||||
import "#flow/stages/authenticator_validate/AuthenticatorValidateStageDuo";
|
||||
import "#flow/stages/authenticator_validate/AuthenticatorValidateStageWebAuthn";
|
||||
|
||||
import Styles from "./AuthenticatorValidateStage.css";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
|
||||
import { BaseStage, StageHost, SubmitOptions } from "#flow/stages/base";
|
||||
@@ -19,8 +21,9 @@ import {
|
||||
} from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { css, CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit";
|
||||
import { CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { repeat } from "lit/directives/repeat.js";
|
||||
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
@@ -29,39 +32,6 @@ 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 customCSS = css`
|
||||
.authenticator-button {
|
||||
/* compatibility-mode-fix */
|
||||
& {
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: var(--pf-global--spacer--md);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--pf-global--Color--light-200);
|
||||
}
|
||||
}
|
||||
:host([theme="dark"]) .authenticator-button {
|
||||
color: var(--ak-dark-foreground) !important;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--pf-global--Color--300);
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
width: 3rem;
|
||||
}
|
||||
.content {
|
||||
text-align: left;
|
||||
}
|
||||
`;
|
||||
|
||||
interface DevicePickerProps {
|
||||
icon?: string;
|
||||
label: string;
|
||||
@@ -121,7 +91,7 @@ export class AuthenticatorValidateStage
|
||||
PFFormControl,
|
||||
PFTitle,
|
||||
PFButton,
|
||||
customCSS,
|
||||
Styles,
|
||||
];
|
||||
|
||||
flowSlug = "";
|
||||
@@ -227,36 +197,42 @@ export class AuthenticatorValidateStage
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const deviceChallengeButtons = this.challenge.deviceChallenges.map((challenges, idx) => {
|
||||
const buttonID = `device-challenge-${idx}`;
|
||||
const labelID = `${buttonID}-label`;
|
||||
const descriptionID = `${buttonID}-description`;
|
||||
const { deviceChallenges } = this.challenge;
|
||||
|
||||
const { icon, label, description } = DevicePickerPropMap[challenges.deviceClass];
|
||||
const deviceChallengeButtons = repeat(
|
||||
deviceChallenges,
|
||||
(challenges) => challenges.deviceUid,
|
||||
(challenges, idx) => {
|
||||
const buttonID = `device-challenge-${idx}`;
|
||||
const labelID = `${buttonID}-label`;
|
||||
const descriptionID = `${buttonID}-description`;
|
||||
|
||||
return html`
|
||||
<button
|
||||
id=${buttonID}
|
||||
aria-labelledby=${labelID}
|
||||
aria-describedby=${descriptionID}
|
||||
class="pf-c-button authenticator-button"
|
||||
type="button"
|
||||
@click=${() => {
|
||||
this.selectedDeviceChallenge = challenges;
|
||||
}}
|
||||
>
|
||||
<i class="fas ${icon}" aria-hidden="true"></i>
|
||||
<div class="content">
|
||||
<p id=${labelID}>${label}</p>
|
||||
<small id=${descriptionID}>${description}</small>
|
||||
</div>
|
||||
</button>
|
||||
`;
|
||||
});
|
||||
const { icon, label, description } = DevicePickerPropMap[challenges.deviceClass];
|
||||
|
||||
return html`
|
||||
<button
|
||||
id=${buttonID}
|
||||
aria-labelledby=${labelID}
|
||||
aria-describedby=${descriptionID}
|
||||
class="pf-c-button authenticator-button"
|
||||
type="button"
|
||||
@click=${() => {
|
||||
this.selectedDeviceChallenge = challenges;
|
||||
}}
|
||||
>
|
||||
<i class="fas ${icon}" aria-hidden="true"></i>
|
||||
<div class="content">
|
||||
<h1 class="pf-c-title pf-m-sm" id=${labelID}>${label}</h1>
|
||||
<p class="pf-c-form__helper-text" id=${descriptionID}>${description}</p>
|
||||
</div>
|
||||
</button>
|
||||
`;
|
||||
},
|
||||
);
|
||||
|
||||
return html`<fieldset class="pf-c-form__group pf-m-action" name="device-challenges">
|
||||
<legend class="pf-c-title">${msg("Select an authentication method")}</legend>
|
||||
${deviceChallengeButtons.length
|
||||
${deviceChallenges.length
|
||||
? deviceChallengeButtons
|
||||
: msg("No authentication methods available.")}
|
||||
</fieldset>`;
|
||||
@@ -267,23 +243,27 @@ export class AuthenticatorValidateStage
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const stageButtons = this.challenge.configurationStages.map((stage) => {
|
||||
return html`<button
|
||||
class="pf-c-button authenticator-button"
|
||||
type="button"
|
||||
@click=${() => {
|
||||
this.submit({
|
||||
component: this.challenge.component || "",
|
||||
selectedStage: stage.pk,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div class="content">
|
||||
<p>${stage.name}</p>
|
||||
<small>${stage.verboseName}</small>
|
||||
</div>
|
||||
</button>`;
|
||||
});
|
||||
const stageButtons = repeat(
|
||||
this.challenge.configurationStages,
|
||||
(stage) => stage.pk,
|
||||
(stage) => {
|
||||
return html`<button
|
||||
class="pf-c-button authenticator-button"
|
||||
type="button"
|
||||
@click=${() => {
|
||||
this.submit({
|
||||
component: this.challenge.component || "",
|
||||
selectedStage: stage.pk,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div class="content">
|
||||
<h1 class="pf-c-title pf-m-sm">${stage.name}</h1>
|
||||
<p class="pf-c-form__helper-text">${stage.verboseName}</p>
|
||||
</div>
|
||||
</button>`;
|
||||
},
|
||||
);
|
||||
|
||||
return html`<fieldset class="pf-c-form__group pf-m-action" name="stages">
|
||||
<legend class="sr-only">${msg("Select a configuration stage")}</legend>
|
||||
|
||||
@@ -24,6 +24,7 @@ import { msg, str } from "@lit/localize";
|
||||
import { css, CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { createRef, ref } from "lit/directives/ref.js";
|
||||
import { repeat } from "lit/directives/repeat.js";
|
||||
|
||||
import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
@@ -286,24 +287,33 @@ export class IdentificationStage extends BaseStage<
|
||||
}
|
||||
|
||||
renderFooter() {
|
||||
if (!this.challenge?.enrollUrl && !this.challenge?.recoveryUrl) {
|
||||
const { enrollUrl, recoveryUrl } = this.challenge || {};
|
||||
|
||||
const enrollmentItem = enrollUrl
|
||||
? html`<div class="pf-c-login__main-footer-band-item">
|
||||
${msg("Need an account?")}
|
||||
<a id="enroll" href="${enrollUrl}">${msg("Sign up.")}</a>
|
||||
</div>`
|
||||
: null;
|
||||
|
||||
const recoveryItem = recoveryUrl
|
||||
? html`<div class="pf-c-login__main-footer-band-item">
|
||||
<a id="recovery" href="${recoveryUrl}">${msg("Forgot username or password?")}</a>
|
||||
</div>`
|
||||
: null;
|
||||
|
||||
if (!enrollmentItem && !recoveryItem) {
|
||||
return nothing;
|
||||
}
|
||||
return html`<div slot="footer-band" class="pf-c-login__main-footer-band">
|
||||
${this.challenge.enrollUrl
|
||||
? html`<p class="pf-c-login__main-footer-band-item">
|
||||
${msg("Need an account?")}
|
||||
<a id="enroll" href="${this.challenge.enrollUrl}">${msg("Sign up.")}</a>
|
||||
</p>`
|
||||
: nothing}
|
||||
${this.challenge.recoveryUrl
|
||||
? html`<p class="pf-c-login__main-footer-band-item">
|
||||
<a id="recovery" href="${this.challenge.recoveryUrl}"
|
||||
>${msg("Forgot username or password?")}</a
|
||||
>
|
||||
</p>`
|
||||
: nothing}
|
||||
</div>`;
|
||||
|
||||
return html`<fieldset
|
||||
slot="footer-band"
|
||||
part="additional-actions"
|
||||
class="pf-c-login__main-footer-band"
|
||||
>
|
||||
<legend class="sr-only">${msg("Additional actions")}</legend>
|
||||
${enrollmentItem} ${recoveryItem}
|
||||
</fieldset>`;
|
||||
}
|
||||
|
||||
renderInput(): TemplateResult {
|
||||
@@ -404,7 +414,7 @@ export class IdentificationStage extends BaseStage<
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<ak-flow-card .challenge=${this.challenge}>
|
||||
return html`<ak-flow-card .challenge=${this.challenge} part="flow-card">
|
||||
<form class="pf-c-form" @submit=${this.submitForm}>
|
||||
${this.challenge.applicationPre
|
||||
? html`<p>
|
||||
@@ -425,13 +435,15 @@ export class IdentificationStage extends BaseStage<
|
||||
`
|
||||
: nothing}
|
||||
</form>
|
||||
${(this.challenge.sources || []).length > 0
|
||||
${this.challenge.sources?.length
|
||||
? html`<ul slot="footer" class="pf-c-login__main-footer-links">
|
||||
${(this.challenge.sources || []).map((source) => {
|
||||
return this.renderSource(source);
|
||||
})}
|
||||
${repeat(
|
||||
this.challenge.sources,
|
||||
(source) => source.name,
|
||||
(source) => this.renderSource(source),
|
||||
)}
|
||||
</ul> `
|
||||
: nothing}
|
||||
: null}
|
||||
${this.renderFooter()}
|
||||
</ak-flow-card>`;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { PasswordManagerPrefill } from "#flow/stages/identification/Identificati
|
||||
import { PasswordChallenge, PasswordChallengeResponseRequest } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, html, nothing, TemplateResult } from "lit";
|
||||
import { CSSResult, html, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
@@ -79,12 +79,17 @@ export class PasswordStage extends BaseStage<PasswordChallenge, PasswordChalleng
|
||||
</fieldset>
|
||||
</form>
|
||||
${this.challenge.recoveryUrl
|
||||
? html`<div slot="footer-band" class="pf-c-login__main-footer-band">
|
||||
<p class="pf-c-login__main-footer-band-item">
|
||||
<a href="${this.challenge.recoveryUrl}"> ${msg("Forgot password?")}</a>
|
||||
</p>
|
||||
</div>`
|
||||
: nothing}
|
||||
? html`<fieldset
|
||||
slot="footer-band"
|
||||
part="additional-actions"
|
||||
class="pf-c-login__main-footer-band"
|
||||
>
|
||||
<legend class="sr-only">${msg("Additional actions")}</legend>
|
||||
<div class="pf-c-login__main-footer-band-item">
|
||||
<a href="${this.challenge.recoveryUrl}">${msg("Forgot password?")}</a>
|
||||
</div>
|
||||
</fieldset>`
|
||||
: null}
|
||||
</ak-flow-card>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ import { msg } from "@lit/localize";
|
||||
import { css, html, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";
|
||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||
import PFSpinner from "@patternfly/patternfly/components/Spinner/spinner.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
@@ -16,12 +14,23 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
export class Loading extends AKElement {
|
||||
static styles = [
|
||||
PFBase,
|
||||
PFPage,
|
||||
PFSpinner,
|
||||
PFEmptyState,
|
||||
css`
|
||||
:host([theme="dark"]) h1 {
|
||||
color: var(--ak-dark-foreground);
|
||||
:host {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
place-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
gap: var(--pf-global--spacer--md);
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: var(--pf-global--FontSize--xl);
|
||||
font-weight: var(--pf-global--FontWeight--normal);
|
||||
font-family: var(--pf-global--FontFamily--heading--sans-serif);
|
||||
}
|
||||
`,
|
||||
];
|
||||
@@ -38,24 +47,17 @@ export class Loading extends AKElement {
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<section
|
||||
class="ak-static-page pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl"
|
||||
>
|
||||
<div class="pf-c-empty-state" style="height: 100vh;">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<span
|
||||
class="pf-c-spinner pf-m-xl"
|
||||
role="progressbar"
|
||||
aria-valuetext="${msg("Loading...")}"
|
||||
>
|
||||
<span class="pf-c-spinner__clipper"></span>
|
||||
<span class="pf-c-spinner__lead-ball"></span>
|
||||
<span class="pf-c-spinner__tail-ball"></span>
|
||||
</span>
|
||||
<h1 class="pf-c-title pf-m-lg">${msg("Loading...")}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</section>`;
|
||||
return html`<span class="pf-c-spinner pf-m-xl" aria-hidden="true">
|
||||
<span class="pf-c-spinner__clipper"></span>
|
||||
<span class="pf-c-spinner__lead-ball"></span>
|
||||
<span class="pf-c-spinner__tail-ball"></span>
|
||||
</span>
|
||||
<label for="progress" class="pf-c-title pf-m-lg">${msg("Loading")}</label>
|
||||
<progress
|
||||
class="sr-only"
|
||||
id="progress"
|
||||
aria-valuetext=${msg("Please wait while the content is loading")}
|
||||
></progress>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
|
||||
.pf-c-card {
|
||||
--pf-c-card--BoxShadow: var(--pf-global--BoxShadow--md-bottom);
|
||||
--pf-c-card--BackgroundColor: var(--pf-global--BackgroundColor--150);
|
||||
|
||||
transition: box-shadow 150ms ease-in-out;
|
||||
border: 0.5px solid var(--pf-global--BorderColor--100);
|
||||
|
||||
Reference in New Issue
Block a user