mirror of
https://github.com/goauthentik/authentik.git
synced 2026-06-17 19:09:11 +03:00
flows: add option for flow layout with frame background (#19527)
* flows: add option for flow layout with frame background Signed-off-by: Jens Langhammer <jens@goauthentik.io> fix Signed-off-by: Jens Langhammer <jens@goauthentik.io> * Tidy variables. Fix mobile and tablet layouts, shadows. * Update web/src/flow/FlowExecutor.ts Co-authored-by: Jens L. <jens@goauthentik.io> Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io> Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com> Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
This commit is contained in:
@@ -31,6 +31,9 @@ class FlowLayout(models.TextChoices):
|
||||
SIDEBAR_LEFT = "sidebar_left"
|
||||
SIDEBAR_RIGHT = "sidebar_right"
|
||||
|
||||
SIDEBAR_LEFT_FRAME_BACKGROUND = "sidebar_left_frame_background"
|
||||
SIDEBAR_RIGHT_FRAME_BACKGROUND = "sidebar_right_frame_background"
|
||||
|
||||
|
||||
class ErrorDetailSerializer(PassiveSerializer):
|
||||
"""Serializer for rest_framework's error messages"""
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# Generated by Django 5.2.10 on 2026-01-16 17:50
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_flows", "0030_alter_flow_background"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="flow",
|
||||
name="layout",
|
||||
field=models.TextField(
|
||||
choices=[
|
||||
("stacked", "Stacked"),
|
||||
("content_left", "Content Left"),
|
||||
("content_right", "Content Right"),
|
||||
("sidebar_left", "Sidebar Left"),
|
||||
("sidebar_right", "Sidebar Right"),
|
||||
("sidebar_left_frame_background", "Sidebar Left Frame Background"),
|
||||
("sidebar_right_frame_background", "Sidebar Right Frame Background"),
|
||||
],
|
||||
default="stacked",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -7966,7 +7966,9 @@
|
||||
"content_left",
|
||||
"content_right",
|
||||
"sidebar_left",
|
||||
"sidebar_right"
|
||||
"sidebar_right",
|
||||
"sidebar_left_frame_background",
|
||||
"sidebar_right_frame_background"
|
||||
],
|
||||
"title": "Layout"
|
||||
},
|
||||
|
||||
@@ -36155,6 +36155,8 @@ components:
|
||||
- content_right
|
||||
- sidebar_left
|
||||
- sidebar_right
|
||||
- sidebar_left_frame_background
|
||||
- sidebar_right_frame_background
|
||||
type: string
|
||||
CountryCodeEnum:
|
||||
enum:
|
||||
@@ -38807,6 +38809,8 @@ components:
|
||||
- content_right
|
||||
- sidebar_left
|
||||
- sidebar_right
|
||||
- sidebar_left_frame_background
|
||||
- sidebar_right_frame_background
|
||||
type: string
|
||||
FlowRequest:
|
||||
type: object
|
||||
|
||||
@@ -293,6 +293,20 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
|
||||
>
|
||||
${LayoutToLabel(FlowLayoutEnum.SidebarRight)}
|
||||
</option>
|
||||
<option
|
||||
value=${FlowLayoutEnum.SidebarLeftFrameBackground}
|
||||
?selected=${this.instance?.layout ===
|
||||
FlowLayoutEnum.SidebarLeftFrameBackground}
|
||||
>
|
||||
${LayoutToLabel(FlowLayoutEnum.SidebarLeftFrameBackground)}
|
||||
</option>
|
||||
<option
|
||||
value=${FlowLayoutEnum.SidebarRightFrameBackground}
|
||||
?selected=${this.instance?.layout ===
|
||||
FlowLayoutEnum.SidebarRightFrameBackground}
|
||||
>
|
||||
${LayoutToLabel(FlowLayoutEnum.SidebarRightFrameBackground)}
|
||||
</option>
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-file-search-input
|
||||
|
||||
@@ -39,6 +39,10 @@ export function LayoutToLabel(layout: FlowLayoutEnum): string {
|
||||
return msg("Sidebar left");
|
||||
case FlowLayoutEnum.SidebarRight:
|
||||
return msg("Sidebar right");
|
||||
case FlowLayoutEnum.SidebarLeftFrameBackground:
|
||||
return msg("Sidebar left (frame background)");
|
||||
case FlowLayoutEnum.SidebarRightFrameBackground:
|
||||
return msg("Sidebar right (frame background)");
|
||||
case FlowLayoutEnum.UnknownDefaultOpenApi:
|
||||
return msg("Unknown layout");
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import { Interface } from "#elements/Interface";
|
||||
import { showAPIErrorMessage } from "#elements/messages/MessageContainer";
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import { WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
import { LitPropertyRecord } from "#elements/types";
|
||||
import { LitPropertyRecord, SlottedTemplateResult } from "#elements/types";
|
||||
import { exportParts } from "#elements/utils/attributes";
|
||||
import { ThemedImage } from "#elements/utils/images";
|
||||
|
||||
@@ -185,9 +185,10 @@ export class FlowExecutor
|
||||
* Synchronize flow info such as background image with the current state.
|
||||
*/
|
||||
#synchronizeFlowInfo() {
|
||||
if (!this.flowInfo) {
|
||||
return;
|
||||
}
|
||||
if (!this.flowInfo) return;
|
||||
|
||||
if (this.layout === FlowLayoutEnum.SidebarLeftFrameBackground) return;
|
||||
if (this.layout === FlowLayoutEnum.SidebarRightFrameBackground) return;
|
||||
|
||||
const background =
|
||||
this.flowInfo.backgroundThemedUrls?.[this.activeTheme] || this.flowInfo.background;
|
||||
@@ -275,10 +276,7 @@ export class FlowExecutor
|
||||
this.layout = this.challenge?.flowInfo?.layout || FlowExecutor.DefaultLayout;
|
||||
}
|
||||
|
||||
if (
|
||||
(changedProperties.has("flowInfo") || changedProperties.has("activeTheme")) &&
|
||||
this.flowInfo
|
||||
) {
|
||||
if (changedProperties.has("flowInfo") || changedProperties.has("activeTheme")) {
|
||||
this.#synchronizeFlowInfo();
|
||||
}
|
||||
|
||||
@@ -539,11 +537,37 @@ export class FlowExecutor
|
||||
|
||||
//#region Render
|
||||
|
||||
protected renderLoading(): TemplateResult {
|
||||
protected renderLoading(): SlottedTemplateResult {
|
||||
return html`<slot class="slotted-content" name="placeholder"></slot>`;
|
||||
}
|
||||
|
||||
protected override render(): TemplateResult {
|
||||
protected renderFrameBackground(): SlottedTemplateResult {
|
||||
return guard([this.layout, this.#challenge], () => {
|
||||
if (
|
||||
this.layout !== FlowLayoutEnum.SidebarLeftFrameBackground &&
|
||||
this.layout !== FlowLayoutEnum.SidebarRightFrameBackground
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const src = this.#challenge?.flowInfo?.background;
|
||||
|
||||
if (!src) return nothing;
|
||||
|
||||
return html`
|
||||
<div class="ak-c-login__content" part="content">
|
||||
<iframe
|
||||
class="ak-c-login__content-iframe"
|
||||
part="content-iframe"
|
||||
name="flow-content-frame"
|
||||
src=${src}
|
||||
></iframe>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
}
|
||||
|
||||
protected override render(): SlottedTemplateResult {
|
||||
const { component } = this.challenge || {};
|
||||
|
||||
return html`<ak-locale-select
|
||||
@@ -551,7 +575,7 @@ export class FlowExecutor
|
||||
exportparts="label:locale-select-label,select:locale-select-select"
|
||||
class="pf-m-dark"
|
||||
></ak-locale-select>
|
||||
|
||||
${this.renderFrameBackground()}
|
||||
<header class="pf-c-login__header">${this.renderInspectorButton()}</header>
|
||||
<main
|
||||
data-layout=${this.layout}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
fieldset[name="login-sources"] {
|
||||
--ak-login-sources-padding-inline: var(--pf-global--spacer--xl);
|
||||
--ak-c-login-sources-padding-inline: var(--pf-global--spacer--xl);
|
||||
|
||||
/* compatibility-mode-fix */
|
||||
|
||||
@@ -10,7 +10,7 @@ fieldset[name="login-sources"] {
|
||||
justify-content: center;
|
||||
gap: var(--pf-global--spacer--sm);
|
||||
|
||||
padding-inline: var(--ak-login-sources-padding-inline) !important;
|
||||
padding-inline: var(--ak-c-login-sources-padding-inline) !important;
|
||||
padding-block-start: var(--pf-global--spacer--md) !important;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ fieldset[name="login-sources"] {
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
--ak-login-sources-padding-inline: var(--pf-global--spacer--2xl);
|
||||
--ak-c-login-sources-padding-inline: var(--pf-global--spacer--2xl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,36 +2,36 @@
|
||||
|
||||
/* compatibility-mode-fix */
|
||||
.pf-c-login.pf-c-login {
|
||||
--ak-login--PaddingMax: 8dvw;
|
||||
--ak-login--padding: clamp(
|
||||
--ak-c-login--PaddingMax: 8dvw;
|
||||
--ak-c-login--padding: clamp(
|
||||
var(--pf-global--spacer--md),
|
||||
var(--pf-global--spacer--2xl),
|
||||
var(--ak-login--PaddingMax)
|
||||
var(--ak-c-login--PaddingMax)
|
||||
);
|
||||
|
||||
--ak-login__main--brand-PaddingMin: var(--pf-global--spacer--xs);
|
||||
--ak-login__main--brand-PaddingIdeal: 5rem;
|
||||
--ak-login__main--brand-PaddingMax: 15dvh;
|
||||
--ak-c-login__main--brand-PaddingMin: var(--pf-global--spacer--xs);
|
||||
--ak-c-login__main--brand-PaddingIdeal: 5rem;
|
||||
--ak-c-login__main--brand-PaddingMax: 15dvh;
|
||||
|
||||
--ak-login__footer--PaddingBlock: var(--pf-global--spacer--md);
|
||||
--ak-c-login__footer--PaddingBlock: var(--pf-global--spacer--md);
|
||||
|
||||
--ak-login--MaxWidth: 35rem;
|
||||
--ak-c-login--MaxWidth: 35rem;
|
||||
|
||||
--ak-login__main-ColumnWidth: minmax(
|
||||
min(100%, var(--ak-login--MaxWidth)),
|
||||
var(--ak-login--MaxWidth)
|
||||
--ak-c-login__main-ColumnWidth: minmax(
|
||||
min(100%, var(--ak-c-login--MaxWidth)),
|
||||
var(--ak-c-login--MaxWidth)
|
||||
);
|
||||
|
||||
--pf-c-login__main-body--PaddingBottom: 0;
|
||||
|
||||
--ak-login__main--footer-PaddingMin: var(--pf-global--spacer--xs);
|
||||
--ak-login__main--footer-PaddingIdeal: 3rem;
|
||||
--ak-login__main--footer-PaddingMax: 9dvh;
|
||||
--ak-c-login__main--footer-PaddingMin: var(--pf-global--spacer--xs);
|
||||
--ak-c-login__main--footer-PaddingIdeal: 3rem;
|
||||
--ak-c-login__main--footer-PaddingMax: 9dvh;
|
||||
|
||||
--pf-c-login__main-footer--PaddingBottom: clamp(
|
||||
var(--ak-login__main--footer-PaddingMin),
|
||||
var(--ak-login__main--footer-PaddingIdeal),
|
||||
var(--ak-login__main--footer-PaddingMax)
|
||||
var(--ak-c-login__main--footer-PaddingMin),
|
||||
var(--ak-c-login__main--footer-PaddingIdeal),
|
||||
var(--ak-c-login__main--footer-PaddingMax)
|
||||
);
|
||||
|
||||
--pf-c-login__main-footer-band--BackgroundColor: transparent;
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
grid-template-columns:
|
||||
1fr
|
||||
[main] var(--ak-login__main-ColumnWidth)
|
||||
[main] var(--ak-c-login__main-ColumnWidth)
|
||||
1fr;
|
||||
|
||||
grid-template-areas:
|
||||
@@ -63,14 +63,10 @@
|
||||
&::before {
|
||||
display: block;
|
||||
content: "";
|
||||
background-color: var(--ak-login--background-color-overlay, transparent);
|
||||
background-color: var(--ak-c-login--BackgroundColorOverlay, transparent);
|
||||
z-index: -1;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&::before,
|
||||
.pf-c-login__overlay {
|
||||
grid-row: header / footer;
|
||||
grid-column: header;
|
||||
}
|
||||
@@ -85,7 +81,7 @@
|
||||
}
|
||||
|
||||
@media (max-width: 35rem) or (max-height: 17.5rem) {
|
||||
--ak-login--background-color-overlay: var(--pf-c-login__main--BackgroundColor);
|
||||
--ak-c-login--BackgroundColorOverlay: var(--pf-c-login__main--BackgroundColor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +98,7 @@
|
||||
|
||||
.pf-c-login__header {
|
||||
grid-area: header;
|
||||
padding-inline: calc(var(--ak-login--padding) / 2);
|
||||
padding-inline: calc(var(--ak-c-login--padding) / 2);
|
||||
align-self: start;
|
||||
|
||||
display: grid;
|
||||
@@ -131,14 +127,16 @@
|
||||
.pf-c-login__main {
|
||||
--pf-c-login__container--PaddingLeft: 0 !important;
|
||||
--pf-c-login__container--PaddingRight: 0 !important;
|
||||
box-shadow: var(--pf-global--BoxShadow--md);
|
||||
--ak-c-login__main--BoxShadow: var(--pf-global--BoxShadow--md);
|
||||
|
||||
box-shadow: var(--ak-c-login__main--BoxShadow) !important;
|
||||
|
||||
grid-area: main;
|
||||
margin: 0;
|
||||
|
||||
position: relative;
|
||||
max-width: var(--ak-login--MaxWidth);
|
||||
min-height: calc(var(--ak-login--MaxWidth) * 0.8);
|
||||
max-width: var(--ak-c-login--MaxWidth);
|
||||
min-height: calc(var(--ak-c-login--MaxWidth) * 0.8);
|
||||
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
@@ -150,7 +148,7 @@
|
||||
}
|
||||
|
||||
@media (max-width: 35rem) or (max-height: 17.5rem) {
|
||||
box-shadow: none;
|
||||
--ak-c-login__main--BoxShadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +157,7 @@
|
||||
/* #region Main Header */
|
||||
|
||||
.pf-c-login__main-header {
|
||||
padding-inline: var(--ak-login--padding);
|
||||
padding-inline: var(--ak-c-login--padding);
|
||||
padding-block: clamp(var(--pf-global--spacer--xs), 6dvw, var(--pf-global--spacer--lg));
|
||||
|
||||
.pf-c-title {
|
||||
@@ -168,26 +166,26 @@
|
||||
}
|
||||
|
||||
.pf-c-login__main-header.pf-c-brand {
|
||||
--ak-login__main-padding-block-start: clamp(
|
||||
var(--ak-login__main--brand-PaddingMin),
|
||||
var(--ak-login__main--brand-PaddingIdeal),
|
||||
var(--ak-login__main--brand-PaddingMax)
|
||||
--ak-c-login__main-padding-block-start: clamp(
|
||||
var(--ak-c-login__main--brand-PaddingMin),
|
||||
var(--ak-c-login__main--brand-PaddingIdeal),
|
||||
var(--ak-c-login__main--brand-PaddingMax)
|
||||
);
|
||||
|
||||
padding-inline: calc(var(--ak-login--padding) / 4);
|
||||
padding-inline: calc(var(--ak-c-login--padding) / 4);
|
||||
padding-block-start: calc(
|
||||
var(--ak-login__main-padding-block-start) - var(--ak-login__footer--PaddingBlock)
|
||||
var(--ak-c-login__main-padding-block-start) - var(--ak-c-login__footer--PaddingBlock)
|
||||
);
|
||||
padding-bottom: var(--pf-global--spacer--xs);
|
||||
|
||||
padding-block-end: calc(var(--ak-login--padding) / 2);
|
||||
padding-block-end: calc(var(--ak-c-login--padding) / 2);
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.branding-logo {
|
||||
display: block;
|
||||
max-width: clamp(75%, calc(var(--ak-login--MaxWidth) / 2), 90%);
|
||||
max-width: clamp(75%, calc(var(--ak-c-login--MaxWidth) / 2), 90%);
|
||||
}
|
||||
|
||||
/* Ensure Font Awesome logos scale similarly to image logos */
|
||||
@@ -205,20 +203,70 @@
|
||||
|
||||
.pf-c-login__main-body {
|
||||
flex: 1 1 auto;
|
||||
padding-inline: var(--ak-login--padding);
|
||||
padding-inline: var(--ak-c-login--padding);
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Frame */
|
||||
|
||||
.ak-c-login__content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
grid-column: content;
|
||||
grid-row: content;
|
||||
display: none;
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
box-shadow: var(--ak-c-login__content-BoxShadow, none);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ak-c-login__content-iframe {
|
||||
display: block;
|
||||
border: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Layout variations */
|
||||
|
||||
.pf-c-login[data-layout$="frame_background"] {
|
||||
--ak-c-login--BackgroundColorOverlay: var(--pf-c-login__main--BackgroundColor);
|
||||
}
|
||||
|
||||
.pf-c-login[data-layout^="sidebar_left"] {
|
||||
--ak-c-login__content-BoxShadow: inset var(--pf-global--BoxShadow--md-right);
|
||||
}
|
||||
|
||||
.pf-c-login[data-layout^="sidebar_right"] {
|
||||
--ak-c-login__content-BoxShadow: inset var(--pf-global--BoxShadow--md-left);
|
||||
}
|
||||
|
||||
.pf-c-login__main[data-layout$="frame_background"] {
|
||||
--ak-c-login__main--BoxShadow: none;
|
||||
}
|
||||
|
||||
@media (min-width: 70rem) and (min-height: 17.5rem) {
|
||||
.pf-c-login__main[data-layout^="sidebar"] {
|
||||
box-shadow: none;
|
||||
--ak-c-login__main--BoxShadow: none;
|
||||
}
|
||||
|
||||
.pf-c-login[data-layout="content_left"],
|
||||
.pf-c-login[data-layout="content_right"] {
|
||||
.ak-c-login__content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pf-c-login[data-layout^="content"] {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
|
||||
@@ -237,12 +285,14 @@
|
||||
|
||||
.pf-c-login[data-layout="sidebar_left"],
|
||||
.pf-c-login[data-layout="sidebar_right"] {
|
||||
--ak-login--MaxWidth: 36rem;
|
||||
--ak-login--background-color-overlay: var(--pf-c-login__main--BackgroundColor);
|
||||
|
||||
&::before {
|
||||
box-shadow: var(--pf-global--BoxShadow--md);
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-login[data-layout^="sidebar"] {
|
||||
--ak-c-login--MaxWidth: 36rem;
|
||||
--ak-c-login--BackgroundColorOverlay: var(--pf-c-login__main--BackgroundColor);
|
||||
|
||||
.pf-c-login__main {
|
||||
height: 100%;
|
||||
@@ -257,22 +307,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-login[data-layout="sidebar_left"] {
|
||||
grid-template-columns: [main footer] var(--ak-login__main-ColumnWidth) repeat(2, 1fr);
|
||||
.pf-c-login[data-layout^="sidebar_left"] {
|
||||
grid-template-columns: [main footer] var(--ak-c-login__main-ColumnWidth) repeat(2, 1fr);
|
||||
|
||||
grid-template-areas:
|
||||
"header . ."
|
||||
"main . ."
|
||||
"footer . .";
|
||||
"header content content"
|
||||
"main content content"
|
||||
"footer content content";
|
||||
}
|
||||
|
||||
.pf-c-login[data-layout="sidebar_right"] {
|
||||
grid-template-columns: repeat(2, 1fr) var(--ak-login__main-ColumnWidth) [main footer];
|
||||
.pf-c-login[data-layout^="sidebar_right"] {
|
||||
grid-template-columns: repeat(2, 1fr) var(--ak-c-login__main-ColumnWidth) [main footer];
|
||||
|
||||
grid-template-areas:
|
||||
". . header"
|
||||
". . main "
|
||||
". . footer";
|
||||
"content content header"
|
||||
"content content main "
|
||||
"content content footer";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,11 +337,11 @@
|
||||
align-self: end;
|
||||
justify-content: center;
|
||||
padding-inline: var(--pf-global--spacer--xl) !important;
|
||||
padding-block: var(--ak-login__footer--PaddingBlock) !important;
|
||||
min-height: calc((var(--ak-login__footer--PaddingBlock) * 2) + 1rem);
|
||||
padding-block: var(--ak-c-login__footer--PaddingBlock) !important;
|
||||
min-height: calc((var(--ak-c-login__footer--PaddingBlock) * 2) + 1rem);
|
||||
line-height: var(--pf-global--LineHeight--md);
|
||||
min-height: calc(
|
||||
(var(--ak-login__footer--PaddingBlock) * 2) + (var(--pf-global--LineHeight--md) * 1rem)
|
||||
(var(--ak-c-login__footer--PaddingBlock) * 2) + (var(--pf-global--LineHeight--md) * 1rem)
|
||||
);
|
||||
|
||||
@media (max-width: 35rem) {
|
||||
|
||||
Reference in New Issue
Block a user