refactor!: update components

This commit is contained in:
@lukin 2023-01-15 00:00:00 +04:00
parent 17dedde7e2
commit b90506a476
69 changed files with 771 additions and 714 deletions

View file

@ -51,10 +51,11 @@ parent=keywind
### Theme
When you do need to customize a palette, you can configure your colors under the `colors` key in the `theme` section of `tailwind.config.js` file:
When you do need to customize a palette, you can configure your colors under the `colors` key in the `theme` section of Tailwind config file:
`tailwind.config.js`
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
@ -70,18 +71,15 @@ Read more about Tailwind CSS configuration in the [documentation](https://tailwi
### Components
You can inherit Keywind components in your own theme. For example, to resize the primary button you should create a styled `theme/mytheme/components/button/primary.ftl` file:
You can update Keywind components in your own child theme. For example, create a copy of the `body` component and change the background:
`theme/mytheme/login/components/atoms/body.ftl`
```
<#macro kw component="button" rest...>
<${component}
class="bg-primary-600 flex justify-center px-6 py-3 relative rounded-lg text-md text-white w-full focus:outline-none focus:ring-2 focus:ring-primary-600 focus:ring-offset-2 hover:bg-primary-700"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<#macro kw>
<body class="bg-primary-100">
<#nested>
</${component}>
</body>
</#macro>
```

View file

@ -0,0 +1,7 @@
<#-- https://github.com/tailwindlabs/heroicons/blob/master/src/20/solid/arrow-top-right-on-square.svg -->
<#macro kw>
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path clip-rule="evenodd" d="M4.25 5.5C3.83579 5.5 3.5 5.83579 3.5 6.25V14.75C3.5 15.1642 3.83579 15.5 4.25 15.5H12.75C13.1642 15.5 13.5 15.1642 13.5 14.75V10.75C13.5 10.3358 13.8358 10 14.25 10C14.6642 10 15 10.3358 15 10.75V14.75C15 15.9926 13.9926 17 12.75 17H4.25C3.00736 17 2 15.9926 2 14.75V6.25C2 5.00736 3.00736 4 4.25 4H9.25C9.66421 4 10 4.33579 10 4.75C10 5.16421 9.66421 5.5 9.25 5.5H4.25Z" fill-rule="evenodd" />
<path clip-rule="evenodd" d="M6.19385 12.7532C6.47175 13.0603 6.94603 13.0841 7.25319 12.8062L16.5 4.43999V7.25C16.5 7.66421 16.8358 8 17.25 8C17.6642 8 18 7.66421 18 7.25V2.75C18 2.33579 17.6642 2 17.25 2H12.75C12.3358 2 12 2.33579 12 2.75C12 3.16421 12.3358 3.5 12.75 3.5H15.3032L6.24682 11.6938C5.93966 11.9717 5.91595 12.446 6.19385 12.7532Z" fill-rule="evenodd" />
</svg>
</#macro>

View file

@ -0,0 +1,6 @@
<#-- https://github.com/tailwindlabs/heroicons/blob/master/src/20/solid/chevron-down.svg -->
<#macro kw>
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path clip-rule="evenodd" d="M5.23017 7.20938C5.52875 6.92228 6.00353 6.93159 6.29063 7.23017L10 11.1679L13.7094 7.23017C13.9965 6.93159 14.4713 6.92228 14.7698 7.20938C15.0684 7.49647 15.0777 7.97125 14.7906 8.26983L10.5406 12.7698C10.3992 12.9169 10.204 13 10 13C9.79599 13 9.60078 12.9169 9.45938 12.7698L5.20938 8.26983C4.92228 7.97125 4.93159 7.49647 5.23017 7.20938Z" fill-rule="evenodd" />
</svg>
</#macro>

View file

@ -1,16 +1,16 @@
<#import "./provider/bitbucket.ftl" as bitbucketIcon>
<#import "./provider/facebook.ftl" as facebookIcon>
<#import "./provider/github.ftl" as githubIcon>
<#import "./provider/gitlab.ftl" as gitlabIcon>
<#import "./provider/google.ftl" as googleIcon>
<#import "./provider/instagram.ftl" as instagramIcon>
<#import "./provider/linkedin.ftl" as linkedinIcon>
<#import "./provider/microsoft.ftl" as microsoftIcon>
<#import "./provider/oidc.ftl" as oidcIcon>
<#import "./provider/openshift.ftl" as openshiftIcon>
<#import "./provider/paypal.ftl" as paypalIcon>
<#import "./provider/stackoverflow.ftl" as stackoverflowIcon>
<#import "./provider/twitter.ftl" as twitterIcon>
<#import "./bitbucket.ftl" as bitbucketIcon>
<#import "./facebook.ftl" as facebookIcon>
<#import "./github.ftl" as githubIcon>
<#import "./gitlab.ftl" as gitlabIcon>
<#import "./google.ftl" as googleIcon>
<#import "./instagram.ftl" as instagramIcon>
<#import "./linkedin.ftl" as linkedinIcon>
<#import "./microsoft.ftl" as microsoftIcon>
<#import "./oidc.ftl" as oidcIcon>
<#import "./openshift.ftl" as openshiftIcon>
<#import "./paypal.ftl" as paypalIcon>
<#import "./stackoverflow.ftl" as stackoverflowIcon>
<#import "./twitter.ftl" as twitterIcon>
<#macro bitbucket>
<@bitbucketIcon.kw />

View file

@ -0,0 +1,22 @@
<#macro kw color="">
<#switch color>
<#case "error">
<#assign colorClass="bg-red-100 text-red-600">
<#break>
<#case "info">
<#assign colorClass="bg-blue-100 text-blue-600">
<#break>
<#case "success">
<#assign colorClass="bg-green-100 text-green-600">
<#break>
<#case "warning">
<#assign colorClass="bg-orange-100 text-orange-600">
<#break>
<#default>
<#assign colorClass="bg-blue-100 text-blue-600">
</#switch>
<div class="${colorClass} p-4 rounded-lg text-sm" role="alert">
<#nested>
</div>
</#macro>

View file

@ -0,0 +1,5 @@
<#macro kw>
<body class="bg-secondary-100 flex flex-col items-center justify-center min-h-screen sm:py-16">
<#nested>
</body>
</#macro>

View file

@ -0,0 +1,5 @@
<#macro kw>
<div class="flex flex-col pt-4 space-y-2">
<#nested>
</div>
</#macro>

View file

@ -0,0 +1,33 @@
<#macro kw color="" component="button" size="" rest...>
<#switch color>
<#case "primary">
<#assign colorClass="bg-primary-600 text-white focus:ring-primary-600 hover:bg-primary-700">
<#break>
<#case "secondary">
<#assign colorClass="bg-secondary-100 text-secondary-600 focus:ring-secondary-600 hover:bg-secondary-200 hover:text-secondary-900">
<#break>
<#default>
<#assign colorClass="bg-primary-600 text-white focus:ring-primary-600 hover:bg-primary-700">
</#switch>
<#switch size>
<#case "medium">
<#assign sizeClass="px-4 py-2 text-sm">
<#break>
<#case "small">
<#assign sizeClass="px-2 py-1 text-xs">
<#break>
<#default>
<#assign sizeClass="px-4 py-2 text-sm">
</#switch>
<${component}
class="${colorClass} ${sizeClass} flex justify-center relative rounded-lg w-full focus:outline-none focus:ring-2 focus:ring-offset-2"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<#nested>
</${component}>
</#macro>

View file

@ -0,0 +1,19 @@
<#macro kw content="" footer="" header="">
<div class="bg-white p-8 rounded-lg space-y-6">
<#if header?has_content>
<div class="space-y-4">
${header}
</div>
</#if>
<#if content?has_content>
<div class="space-y-4">
${content}
</div>
</#if>
<#if footer?has_content>
<div class="space-y-4">
${footer}
</div>
</#if>
</div>
</#macro>

View file

@ -1,17 +1,19 @@
<#macro kw name checked=false rest...>
<#macro kw checked=false label="" name="" rest...>
<div class="flex items-center">
<input
<#if checked>checked</#if>
class="border-secondary-200 h-4 rounded text-primary-600 w-4 focus:ring-primary-200 focus:ring-opacity-50"
id="${name}"
name="${name}"
type="checkbox"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<label class="block ml-2 text-secondary-900 text-sm" for="${name}">
<#nested>
<label class="ml-2 text-secondary-600 text-sm" for="${name}">
${label}
</label>
</div>
</#macro>

View file

@ -0,0 +1,5 @@
<#macro kw>
<div class="max-w-md space-y-6 w-full">
<#nested>
</div>
</#macro>

View file

@ -0,0 +1,11 @@
<#macro kw rest...>
<form
class="m-0 space-y-4"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<#nested>
</form>
</#macro>

View file

@ -0,0 +1,5 @@
<#macro kw>
<h1 class="text-center text-xl">
<#nested>
</h1>
</#macro>

View file

@ -0,0 +1,37 @@
<#macro
kw
autofocus=false
disabled=false
invalid=false
label=""
message=""
name=""
required=true
rest...
>
<div>
<label class="sr-only" for="${name}">
${label}
</label>
<input
<#if autofocus>autofocus</#if>
<#if disabled>disabled</#if>
<#if required>required</#if>
aria-invalid="${invalid?c}"
class="block border-secondary-200 mt-1 rounded-md w-full focus:border-primary-300 focus:ring focus:ring-primary-200 focus:ring-opacity-50 sm:text-sm"
id="${name}"
name="${name}"
placeholder="${label}"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<#if invalid?? && message??>
<div class="mt-2 text-red-600 text-sm">
${message?no_esc}
</div>
</#if>
</div>
</#macro>

View file

@ -0,0 +1,30 @@
<#macro kw color="" component="a" size="" rest...>
<#switch color>
<#case "primary">
<#assign colorClass="text-primary-600 hover:text-primary-500">
<#break>
<#case "secondary">
<#assign colorClass="text-secondary-600 hover:text-secondary-900">
<#break>
<#default>
<#assign colorClass="text-primary-600 hover:text-primary-500">
</#switch>
<#switch size>
<#case "small">
<#assign sizeClass="text-sm">
<#break>
<#default>
<#assign sizeClass="">
</#switch>
<${component}
class="<#compress>${colorClass} ${sizeClass} inline-flex</#compress>"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<#nested>
</${component}>
</#macro>

View file

@ -1,5 +1,5 @@
<#macro kw>
<div class="font-bold text-center text-2xl">
${kcSanitize(msg("loginTitleHtml", (realm.displayNameHtml!"")))?no_esc}
<#nested>
</div>
</#macro>

View file

@ -1,20 +1,18 @@
<#macro kw id tabIndex checked=false rest...>
<#macro kw checked=false id="" label="" rest...>
<div>
<input
<#if checked>checked</#if>
class="border-secondary-200 focus:ring-primary-600"
id="${id}"
type="radio"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<label
class="font-medium ml-2 text-sm"
for="${id}"
tabindex="${tabIndex}"
>
<#nested>
<label class="ml-2 text-secondary-600 text-sm" for="${id}">
${label}
</label>
</div>
</#macro>

View file

@ -1,10 +0,0 @@
<#macro kw component="button" rest...>
<${component}
class="bg-primary-600 flex justify-center px-4 py-2 relative rounded-lg text-sm text-white w-full focus:outline-none focus:ring-2 focus:ring-primary-600 focus:ring-offset-2 hover:bg-primary-700"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<#nested>
</${component}>
</#macro>

View file

@ -1,10 +0,0 @@
<#macro kw component="button" rest...>
<${component}
class="bg-secondary-100 flex justify-center px-4 py-2 relative rounded-lg text-sm text-secondary-600 w-full focus:outline-none focus:ring-2 focus:ring-secondary-600 focus:ring-offset-2 hover:bg-secondary-200 hover:text-secondary-900"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<#nested>
</${component}>
</#macro>

View file

@ -1,6 +0,0 @@
<#-- https://github.com/tailwindlabs/heroicons/blob/master/src/outline/chevron-down.svg -->
<#macro kw>
<svg fill="none" height="1em" stroke="currentColor" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</#macro>

View file

@ -1,6 +0,0 @@
<#-- https://github.com/tailwindlabs/heroicons/blob/master/src/outline/external-link.svg -->
<#macro kw>
<svg fill="none" height="1em" stroke="currentColor" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
</svg>
</#macro>

View file

@ -1,23 +0,0 @@
<#macro kw invalid name autofocus=false disabled=false message=true required=true rest...>
<label class="sr-only" for="${name}">
<#nested>
</label>
<input
<#if autofocus>autofocus</#if>
<#if disabled>disabled</#if>
<#if required>required</#if>
aria-invalid="${messagesPerField.existsError(invalid)?c}"
class="block border-secondary-200 mt-1 rounded-md w-full focus:border-primary-300 focus:ring focus:ring-primary-200 focus:ring-opacity-50 sm:text-sm"
id="${name}"
name="${name}"
placeholder="<#compress><#nested></#compress>"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<#if message && messagesPerField.existsError(invalid)>
<div class="mt-2 text-red-600 text-sm">
${kcSanitize(messagesPerField.getFirstError(invalid))?no_esc}
</div>
</#if>
</#macro>

View file

@ -1,22 +0,0 @@
<#macro kw>
<#switch message.type>
<#case "error">
<#assign color="bg-red-100 text-red-600">
<#break>
<#case "info">
<#assign color="bg-blue-100 text-blue-600">
<#break>
<#case "success">
<#assign color="bg-green-100 text-green-600">
<#break>
<#case "warning">
<#assign color="bg-orange-100 text-orange-600">
<#break>
<#default>
<#assign color="bg-blue-100 text-blue-600">
</#switch>
<div class="${color} p-4 rounded-lg text-sm" role="alert">
<span>${kcSanitize(message.summary)?no_esc}</span>
</div>
</#macro>

View file

@ -1,10 +0,0 @@
<#import "../button/primary.ftl" as buttonPrimary>
<#macro kw>
<form action="${url.loginAction}" class="flex justify-center" method="post">
<input name="tryAnotherWay" type="hidden" value="on"/>
<@buttonPrimary.kw type="submit">
${msg("doTryAnotherWay")}
</@buttonPrimary.kw>
</form>
</#macro>

View file

@ -1,5 +0,0 @@
<#macro kw>
<footer class="space-y-4">
<#nested>
</footer>
</#macro>

View file

@ -1,5 +0,0 @@
<#macro kw>
<header class="space-y-4">
<#nested>
</header>
</#macro>

View file

@ -1,5 +0,0 @@
<#macro kw>
<main class="space-y-4">
<#nested>
</main>
</#macro>

View file

@ -1,5 +0,0 @@
<#macro kw>
<div class="bg-white p-8 rounded-lg space-y-6">
<#nested>
</div>
</#macro>

View file

@ -1,7 +0,0 @@
<#macro kw>
<div class="bg-secondary-100 flex flex-col items-center justify-center min-h-screen sm:py-16">
<div class="max-w-md space-y-6 w-full">
<#nested>
</div>
</div>
</#macro>

View file

@ -1,29 +0,0 @@
<#import "../icon/chevron-down.ftl" as iconChevronDown>
<#import "../link/secondary.ftl" as linkSecondary>
<#macro kw>
<div class="relative" x-data="{open: false}">
<@linkSecondary.kw component="button" type="button" @click="open = true">
<div class="flex items-center">
<span class="mr-1 text-sm">${locale.current}</span>
<@iconChevronDown.kw />
</div>
</@linkSecondary.kw>
<div
class="absolute bg-white bottom-0 -left-4 max-h-80 mb-6 overflow-y-scroll rounded-lg shadow-lg"
x-cloak
x-show="open"
@click.away="open = false"
>
<#list locale.supported as locales>
<#if locale.current != locales.label>
<div class="px-4 py-2">
<@linkSecondary.kw href=locales.url>
<span class="text-sm">${locales.label}</span>
</@linkSecondary.kw>
</div>
</#if>
</#list>
</div>
</div>
</#macro>

View file

@ -1,3 +0,0 @@
<#macro kw>
<div class="text-secondary-600 text-sm">* ${msg("requiredFields")}</div>
</#macro>

View file

@ -1,3 +0,0 @@
<#macro kw>
<h1 class="text-center text-xl"><#nested "header"></h1>
</#macro>

View file

@ -1,15 +0,0 @@
<#import "../icon/external-link.ftl" as iconExternalLink>
<#import "../link/primary.ftl" as linkPrimary>
<#macro kw>
<#nested "show-username">
<div class="flex items-center justify-center mb-4 space-x-2">
<b>${auth.attemptedUsername}</b>
<@linkPrimary.kw
href="${url.loginRestartFlowUrl}"
title="${msg('restartLoginTooltip')}"
>
<@iconExternalLink.kw />
</@linkPrimary.kw>
</div>
</#macro>

View file

@ -1,10 +0,0 @@
<#macro kw component="a" rest...>
<${component}
class="inline-flex text-primary-600 hover:text-primary-500"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<#nested>
</${component}>
</#macro>

View file

@ -1,10 +0,0 @@
<#macro kw component="a" rest...>
<${component}
class="inline-flex text-secondary-600 hover:text-secondary-900"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<#nested>
</${component}>
</#macro>

View file

@ -0,0 +1,72 @@
<#import "/assets/providers/providers.ftl" as providerIcons>
<#macro kw providers=[]>
<div class="pt-4 separate text-secondary-600 text-sm">
${msg("identity-provider-login-label")}
</div>
<div class="gap-4 grid grid-cols-3">
<#list providers as provider>
<#switch provider.alias>
<#case "bitbucket">
<#assign colorClass="hover:bg-provider-bitbucket/10">
<#break>
<#case "facebook">
<#assign colorClass="hover:bg-provider-facebook/10">
<#break>
<#case "github">
<#assign colorClass="hover:bg-provider-github/10">
<#break>
<#case "gitlab">
<#assign colorClass="hover:bg-provider-gitlab/10">
<#break>
<#case "google">
<#assign colorClass="hover:bg-provider-google/10">
<#break>
<#case "instagram">
<#assign colorClass="hover:bg-provider-instagram/10">
<#break>
<#case "linkedin">
<#assign colorClass="hover:bg-provider-linkedin/10">
<#break>
<#case "microsoft">
<#assign colorClass="hover:bg-provider-microsoft/10">
<#break>
<#case "oidc">
<#assign colorClass="hover:bg-provider-oidc/10">
<#break>
<#case "openshift-v3">
<#assign colorClass="hover:bg-provider-openshift/10">
<#break>
<#case "openshift-v4">
<#assign colorClass="hover:bg-provider-openshift/10">
<#break>
<#case "paypal">
<#assign colorClass="hover:bg-provider-paypal/10">
<#break>
<#case "stackoverflow">
<#assign colorClass="hover:bg-provider-stackoverflow/10">
<#break>
<#case "twitter">
<#assign colorClass="hover:bg-provider-twitter/10">
<#break>
<#default>
<#assign colorClass="hover:bg-secondary-100">
</#switch>
<a
class="${colorClass} border border-secondary-200 flex justify-center py-2 rounded-lg hover:border-transparent"
data-provider="${provider.alias}"
href="${provider.loginUrl}"
type="button"
>
<#if providerIcons[provider.alias]??>
<div class="h-6 w-6">
<@providerIcons[provider.alias] />
</div>
<#else>
${provider.displayName!}
</#if>
</a>
</#list>
</div>
</#macro>

View file

@ -0,0 +1,29 @@
<#import "/assets/icons/chevron-down.ftl" as icon>
<#import "/components/atoms/link.ftl" as link>
<#macro kw currentLocale="" locales=[]>
<div class="relative" x-data="{ open: false }">
<@link.kw @click="open = true" color="secondary" component="button" type="button">
<div class="flex items-center">
<span class="mr-1 text-sm">${currentLocale}</span>
<@icon.kw />
</div>
</@link.kw>
<div
@click.away="open = false"
class="absolute bg-white bottom-0 -left-4 max-h-80 mb-6 overflow-y-scroll rounded-lg shadow-lg"
x-cloak
x-show="open"
>
<#list locales as locale>
<#if currentLocale != locale.label>
<div class="px-4 py-2">
<@link.kw color="secondary" href=locale.url size="small">
${locale.label}
</@link.kw>
</div>
</#if>
</#list>
</div>
</div>
</#macro>

View file

@ -0,0 +1,15 @@
<#import "/assets/icons/arrow-top-right-on-square.ftl" as icon>
<#import "/components/atoms/link.ftl" as link>
<#macro kw linkHref="" linkTitle="" name="">
<div class="flex items-center justify-center mb-4 space-x-2">
<b>${name}</b>
<@link.kw
color="primary"
href=linkHref
title=linkTitle
>
<@icon.kw />
</@link.kw>
</div>
</#macro>

View file

@ -1,70 +0,0 @@
<#import "./icon/provider.ftl" as iconProvider>
<#macro kw>
<div class="pt-4 separate text-secondary-600 text-sm">${msg("identity-provider-login-label")}</div>
<div class="gap-4 grid grid-cols-3">
<#list social.providers as provider>
<#switch provider.alias>
<#case "bitbucket">
<#assign color="hover:bg-provider-bitbucket/10">
<#break>
<#case "facebook">
<#assign color="hover:bg-provider-facebook/10">
<#break>
<#case "github">
<#assign color="hover:bg-provider-github/10">
<#break>
<#case "gitlab">
<#assign color="hover:bg-provider-gitlab/10">
<#break>
<#case "google">
<#assign color="hover:bg-provider-google/10">
<#break>
<#case "instagram">
<#assign color="hover:bg-provider-instagram/10">
<#break>
<#case "linkedin">
<#assign color="hover:bg-provider-linkedin/10">
<#break>
<#case "microsoft">
<#assign color="hover:bg-provider-microsoft/10">
<#break>
<#case "oidc">
<#assign color="hover:bg-provider-oidc/10">
<#break>
<#case "openshift-v3">
<#assign color="hover:bg-provider-openshift/10">
<#break>
<#case "openshift-v4">
<#assign color="hover:bg-provider-openshift/10">
<#break>
<#case "paypal">
<#assign color="hover:bg-provider-paypal/10">
<#break>
<#case "stackoverflow">
<#assign color="hover:bg-provider-stackoverflow/10">
<#break>
<#case "twitter">
<#assign color="hover:bg-provider-twitter/10">
<#break>
<#default>
<#assign color="hover:bg-secondary-100">
</#switch>
<a
class="border border-secondary-200 flex justify-center py-2 rounded-lg hover:border-transparent ${color}"
data-provider="${provider.alias}"
href="${provider.loginUrl}"
type="button"
>
<#if iconProvider[provider.alias]??>
<div class="h-6 w-6">
<@iconProvider[provider.alias] />
</div>
<#else>
${provider.displayName!}
</#if>
</a>
</#list>
</div>
</#macro>

View file

@ -1,4 +1,4 @@
<#macro kw>
<#macro kw script="">
<title>${msg("loginTitle", (realm.displayName!""))}</title>
<meta charset="utf-8" />
@ -23,9 +23,13 @@
</#list>
</#if>
<#if script?has_content>
<script defer src="${url.resourcesPath}/${script}" type="module"></script>
</#if>
<#if properties.scripts?has_content>
<#list properties.scripts?split(" ") as script>
<script defer src="${url.resourcesPath}/${script}" type="text/javascript"></script>
<script defer src="${url.resourcesPath}/${script}" type="module"></script>
</#list>
</#if>
</#macro>

View file

@ -1,10 +1,14 @@
<#import "template.ftl" as layout>
<#import "components/button/primary.ftl" as buttonPrimary>
<#import "components/button/secondary.ftl" as buttonSecondary>
<#import "components/input/primary.ftl" as inputPrimary>
<#import "components/label/totp.ftl" as labelTotp>
<#import "components/label/userdevice.ftl" as labelUserDevice>
<#import "components/link/primary.ftl" as linkPrimary>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<#import "components/atoms/link.ftl" as link>
<#import "features/labels/totp.ftl" as totpLabel>
<#import "features/labels/totp-device.ftl" as totpDeviceLabel>
<#assign totpLabel><@totpLabel.kw /></#assign>
<#assign totpDeviceLabel><@totpDeviceLabel.kw /></#assign>
<@layout.registrationLayout
displayMessage=!messagesPerField.existsError("totp", "userLabel")
@ -24,15 +28,15 @@
</#list>
</ul>
</li>
<#if mode?? && mode = "manual">
<li class="space-y-2">
<#if mode?? && mode="manual">
<li>
<p>${msg("loginTotpManualStep2")}</p>
<p class="font-bold text-xl">${totp.totpSecretEncoded}</p>
</li>
<li>
<@linkPrimary.kw href=totp.qrUrl>
<@link.kw color="primary" href=totp.qrUrl>
${msg("loginTotpScanBarcode")}
</@linkPrimary.kw>
</@link.kw>
</li>
<li class="space-y-2">
<p>${msg("loginTotpManualStep3")}</p>
@ -40,9 +44,9 @@
<li>${msg("loginTotpType")}: ${msg("loginTotp." + totp.policy.type)}</li>
<li>${msg("loginTotpAlgorithm")}: ${totp.policy.getAlgorithmKey()}</li>
<li>${msg("loginTotpDigits")}: ${totp.policy.digits}</li>
<#if totp.policy.type = "totp">
<#if totp.policy.type="totp">
<li>${msg("loginTotpInterval")}: ${totp.policy.period}</li>
<#elseif totp.policy.type = "hotp">
<#elseif totp.policy.type="hotp">
<li>${msg("loginTotpCounter")}: ${totp.policy.initialCounter}</li>
</#if>
</ul>
@ -55,58 +59,52 @@
class="mx-auto"
src="data:image/png;base64, ${totp.totpSecretQrCode}"
>
<@linkPrimary.kw href=totp.manualUrl>
<@link.kw color="primary" href=totp.manualUrl>
${msg("loginTotpUnableToScan")}
</@linkPrimary.kw>
</@link.kw>
</li>
</#if>
<li>${msg("loginTotpStep3")}</li>
<li>${msg("loginTotpStep3DeviceName")}</li>
</ol>
<form action="${url.loginAction}" class="m-0 space-y-4" method="post">
<div>
<@inputPrimary.kw
autocomplete="off"
autofocus=true
invalid=["totp"]
name="totp"
required=false
type="text"
>
<@labelTotp.kw />
</@inputPrimary.kw>
<input name="totpSecret" type="hidden" value="${totp.totpSecret}">
<#if mode??>
<input name="mode" type="hidden" value="${mode}">
</#if>
</div>
<div>
<@inputPrimary.kw
autocomplete="off"
invalid=["userLabel"]
name="userLabel"
required=false
type="text"
>
<@labelUserDevice.kw />
</@inputPrimary.kw>
</div>
<#if isAppInitiatedAction??>
<div class="flex flex-col pt-4 space-y-2">
<@buttonPrimary.kw type="submit">
${msg("doSubmit")}
</@buttonPrimary.kw>
<@buttonSecondary.kw name="cancel-aia" type="submit">
${msg("doCancel")}
</@buttonSecondary.kw>
</div>
<#else>
<div class="pt-4">
<@buttonPrimary.kw type="submit">
${msg("doSubmit")}
</@buttonPrimary.kw>
</div>
<@form.kw action=url.loginAction method="post">
<input name="totpSecret" type="hidden" value="${totp.totpSecret}">
<#if mode??>
<input name="mode" type="hidden" value="${mode}">
</#if>
</form>
<@input.kw
autocomplete="off"
autofocus=true
invalid=messagesPerField.existsError("totp")
label=totpLabel
message=kcSanitize(messagesPerField.get("totp"))
name="totp"
required=false
type="text"
/>
<@input.kw
autocomplete="off"
invalid=messagesPerField.existsError("userLabel")
label=totpDeviceLabel
message=kcSanitize(messagesPerField.get("userLabel"))
name="userLabel"
required=false
type="text"
/>
<@buttonGroup.kw>
<#if isAppInitiatedAction??>
<@button.kw color="primary" type="submit">
${msg("doSubmit")}
</@button.kw>
<@button.kw color="secondary" name="cancel-aia" type="submit" value="true">
${msg("doCancel")}
</@button.kw>
<#else>
<@button.kw color="primary" type="submit">
${msg("doSubmit")}
</@button.kw>
</#if>
</@buttonGroup.kw>
</@form.kw>
</#if>
</@layout.registrationLayout>

View file

@ -1,21 +1,18 @@
<#import "template.ftl" as layout>
<#import "components/button/primary.ftl" as buttonPrimary>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/form.ftl" as form>
<@layout.registrationLayout; section>
<#if section="header">
${msg("confirmLinkIdpTitle")}
<#elseif section="form">
<form action="${url.loginAction}" class="m-0 space-y-4" method="post">
<div>
<@buttonPrimary.kw name="submitAction" type="submit" value="updateProfile">
${msg("confirmLinkIdpReviewProfile")}
</@buttonPrimary.kw>
</div>
<div>
<@buttonPrimary.kw name="submitAction" type="submit" value="linkAccount">
${msg("confirmLinkIdpContinue", idpDisplayName)}
</@buttonPrimary.kw>
</div>
</form>
<@form.kw action=url.loginAction method="post">
<@button.kw color="primary" name="submitAction" type="submit" value="updateProfile">
${msg("confirmLinkIdpReviewProfile")}
</@button.kw>
<@button.kw color="primary" name="submitAction" type="submit" value="linkAccount">
${msg("confirmLinkIdpContinue", idpDisplayName)}
</@button.kw>
</@form.kw>
</#if>
</@layout.registrationLayout>

View file

@ -1,6 +1,7 @@
<#import "template.ftl" as layout>
<#import "components/button/primary.ftl" as buttonPrimary>
<#import "components/button/secondary.ftl" as buttonSecondary>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/form.ftl" as form>
<@layout.registrationLayout; section>
<#if section="header">
@ -46,16 +47,16 @@
</#if>
</h3>
</#if>
<form action="${url.oauthAction}" class="m-0 space-y-4" method="POST">
<@form.kw action=url.oauthAction method="post">
<input name="code" type="hidden" value="${oauth.code}">
<div class="flex flex-col pt-4 space-y-2">
<@buttonPrimary.kw name="accept" type="submit">
<@buttonGroup.kw>
<@button.kw color="primary" name="accept" type="submit">
${msg("doYes")}
</@buttonPrimary.kw>
<@buttonSecondary.kw name="cancel" type="submit">
</@button.kw>
<@button.kw color="secondary" name="cancel" type="submit">
${msg("doNo")}
</@buttonSecondary.kw>
</div>
</form>
</@button.kw>
</@buttonGroup.kw>
</@form.kw>
</#if>
</@layout.registrationLayout>

View file

@ -1,9 +1,12 @@
<#import "template.ftl" as layout>
<#import "components/button/primary.ftl" as buttonPrimary>
<#import "components/input/primary.ftl" as inputPrimary>
<#import "components/label/totp.ftl" as labelTotp>
<#import "components/link/secondary.ftl" as linkSecondary>
<#import "components/radio/primary.ftl" as radioPrimary>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<#import "components/atoms/radio.ftl" as radio>
<#import "features/labels/totp.ftl" as totpLabel>
<#assign totpLabel><@totpLabel.kw /></#assign>
<@layout.registrationLayout
displayMessage=!messagesPerField.existsError("totp")
@ -13,45 +16,35 @@
<#if section="header">
${msg("doLogIn")}
<#elseif section="form">
<form
action="${url.loginAction}"
class="m-0 space-y-4"
method="post"
>
<@form.kw action=url.loginAction method="post">
<#if otpLogin.userOtpCredentials?size gt 1>
<div class="flex items-center space-x-4">
<#list otpLogin.userOtpCredentials as otpCredential>
<@radioPrimary.kw
<@radio.kw
checked=(otpCredential.id == otpLogin.selectedCredentialId)
id="kw-otp-credential-${otpCredential?index}"
label=otpCredential.userLabel
name="selectedCredentialId"
tabIndex="${otpCredential?index}"
value="${otpCredential.id}"
>
${otpCredential.userLabel}
</@radioPrimary.kw>
tabindex=otpCredential?index
value=otpCredential.id
/>
</#list>
</div>
</#if>
<div>
<@inputPrimary.kw
autocomplete="off"
autofocus=true
invalid=["totp"]
name="otp"
type="text"
>
<@labelTotp.kw />
</@inputPrimary.kw>
</div>
<div class="pt-4">
<@buttonPrimary.kw
name="submitAction"
type="submit"
>
<@input.kw
autocomplete="off"
autofocus=true
invalid=messagesPerField.existsError("totp")
label=totpLabel
message=kcSanitize(messagesPerField.get("totp"))
name="otp"
type="text"
/>
<@buttonGroup.kw>
<@button.kw color="primary" name="submitAction" type="submit">
${msg("doLogIn")}
</@buttonPrimary.kw>
</div>
</form>
</@button.kw>
</@buttonGroup.kw>
</@form.kw>
</#if>
</@layout.registrationLayout>

View file

@ -1,8 +1,12 @@
<#import "template.ftl" as layout>
<#import "components/button/primary.ftl" as buttonPrimary>
<#import "components/input/primary.ftl" as inputPrimary>
<#import "components/label/username.ftl" as labelUsername>
<#import "components/link/secondary.ftl" as linkSecondary>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<#import "components/atoms/link.ftl" as link>
<#import "features/labels/username.ftl" as usernameLabel>
<#assign usernameLabel><@usernameLabel.kw /></#assign>
<@layout.registrationLayout
displayInfo=true
@ -13,30 +17,28 @@
<#if section="header">
${msg("emailForgotTitle")}
<#elseif section="form">
<form action="${url.loginAction}" class="m-0 space-y-4" method="post">
<div>
<@inputPrimary.kw
autocomplete=realm.loginWithEmailAllowed?string("email", "username")
autofocus=true
invalid=["username"]
name="username"
type="text"
value=(auth?has_content && auth.showUsername())?then(auth.attemptedUsername, '')
>
<@labelUsername.kw />
</@inputPrimary.kw>
</div>
<div>
<@buttonPrimary.kw type="submit">
<@form.kw action=url.loginAction method="post">
<@input.kw
autocomplete=realm.loginWithEmailAllowed?string("email", "username")
autofocus=true
invalid=messagesPerField.existsError("username")
label=usernameLabel
message=kcSanitize(messagesPerField.get("username"))
name="username"
type="text"
value=(auth?has_content && auth.showUsername())?then(auth.attemptedUsername, '')
/>
<@buttonGroup.kw>
<@button.kw color="primary" type="submit">
${msg("doSubmit")}
</@buttonPrimary.kw>
</div>
</form>
</@button.kw>
</@buttonGroup.kw>
</@form.kw>
<#elseif section="info">
${msg("emailInstruction")}
<#elseif section="nav">
<@linkSecondary.kw href=url.loginUrl>
<span class="text-sm">${kcSanitize(msg("backToLogin"))?no_esc}</span>
</@linkSecondary.kw>
<@link.kw color="secondary" href=url.loginUrl size="small">
${kcSanitize(msg("backToLogin"))?no_esc}
</@link.kw>
</#if>
</@layout.registrationLayout>

View file

@ -1,6 +1,9 @@
<#import "template.ftl" as layout>
<#import "components/button/primary.ftl" as buttonPrimary>
<#import "components/input/primary.ftl" as inputPrimary>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/checkbox.ftl" as checkbox>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<@layout.registrationLayout
displayMessage=!messagesPerField.existsError("password", "password-confirm")
@ -10,7 +13,7 @@
<#if section="header">
${msg("updatePasswordTitle")}
<#elseif section="form">
<form action="${url.loginAction}" class="m-0 space-y-4" method="post">
<@form.kw action=url.loginAction method="post">
<input
autocomplete="username"
name="username"
@ -18,36 +21,44 @@
value="${username}"
>
<input autocomplete="current-password" name="password" type="hidden">
<div>
<@inputPrimary.kw
autocomplete="new-password"
autofocus=true
invalid=["password", "password-confirm"]
message=false
name="password-new"
type="password"
>
${msg("passwordNew")}
</@inputPrimary.kw>
</div>
<div>
<@inputPrimary.kw
autocomplete="new-password"
invalid=["password-confirm"]
name="password-confirm"
type="password"
>
${msg("passwordConfirm")}
</@inputPrimary.kw>
</div>
<#-- TODO isAppInitiatedAction -->
<div>
<@buttonPrimary.kw type="submit">
${msg("doSubmit")}
</@buttonPrimary.kw>
</div>
</form>
<@input.kw
autocomplete="new-password"
autofocus=true
invalid=messagesPerField.existsError("password", "password-confirm")
label=msg("passwordNew")
name="password-new"
type="password"
/>
<@input.kw
autocomplete="new-password"
invalid=messagesPerField.existsError("password-confirm")
label=msg("passwordConfirm")
message=kcSanitize(messagesPerField.get("password-confirm"))
name="password-confirm"
type="password"
/>
<#if isAppInitiatedAction??>
<@checkbox.kw
checked=true
label=msg("logoutOtherSessions")
name="logout-sessions"
value="on"
/>
</#if>
<@buttonGroup.kw>
<#if isAppInitiatedAction??>
<@button.kw color="primary" type="submit">
${msg("doSubmit")}
</@button.kw>
<@button.kw color="secondary" name="cancel-aia" type="submit" value="true">
${msg("doCancel")}
</@button.kw>
<#else>
<@button.kw color="primary" type="submit">
${msg("doSubmit")}
</@button.kw>
</#if>
</@buttonGroup.kw>
</@form.kw>
</#if>
</@layout.registrationLayout>

View file

@ -1,6 +1,8 @@
<#import "template.ftl" as layout>
<#import "components/button/primary.ftl" as buttonPrimary>
<#import "components/input/primary.ftl" as inputPrimary>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<@layout.registrationLayout
displayMessage=!messagesPerField.existsError("email", "firstName", "lastName", "username")
@ -10,62 +12,60 @@
<#if section="header">
${msg("loginProfileTitle")}
<#elseif section="form">
<form action="${url.loginAction}" class="m-0 space-y-4" method="post">
<@form.kw action=url.loginAction method="post">
<#if user.editUsernameAllowed>
<div>
<@inputPrimary.kw
autocomplete="username"
autofocus=true
invalid=["username"]
name="username"
type="text"
value=(user.username)!''
>
${msg("username")}
</@inputPrimary.kw>
</div>
<@input.kw
autocomplete="username"
autofocus=true
invalid=messagesPerField.existsError("username")
label=msg("username")
message=kcSanitize(messagesPerField.get("username"))
name="username"
type="text"
value=(user.username)!''
/>
</#if>
<div>
<@inputPrimary.kw
autocomplete="email"
invalid=["email"]
name="email"
type="email"
value=(user.email)!''
>
${msg("email")}
</@inputPrimary.kw>
</div>
<div>
<@inputPrimary.kw
autocomplete="given-name"
invalid=["firstName"]
name="firstName"
type="text"
value=(user.firstName)!''
>
${msg("firstName")}
</@inputPrimary.kw>
</div>
<div>
<@inputPrimary.kw
autocomplete="family-name"
invalid=["lastName"]
name="lastName"
type="text"
value=(user.lastName)!''
>
${msg("lastName")}
</@inputPrimary.kw>
</div>
<#-- TODO isAppInitiatedAction -->
<div>
<@buttonPrimary.kw type="submit">
${msg("doSubmit")}
</@buttonPrimary.kw>
</div>
</form>
<@input.kw
autocomplete="email"
invalid=messagesPerField.existsError("email")
label=msg("email")
message=kcSanitize(messagesPerField.get("email"))
name="email"
type="email"
value=(user.email)!''
/>
<@input.kw
autocomplete="given-name"
invalid=messagesPerField.existsError("firstName")
label=msg("firstName")
message=kcSanitize(messagesPerField.get("firstName"))
name="firstName"
type="text"
value=(user.firstName)!''
/>
<@input.kw
autocomplete="family-name"
invalid=messagesPerField.existsError("lastName")
label=msg("lastName")
message=kcSanitize(messagesPerField.get("lastName"))
name="lastName"
type="text"
value=(user.lastName)!''
/>
<@buttonGroup.kw>
<#if isAppInitiatedAction??>
<@button.kw color="primary" type="submit">
${msg("doSubmit")}
</@button.kw>
<@button.kw color="secondary" name="cancel-aia" type="submit" value="true">
${msg("doCancel")}
</@button.kw>
<#else>
<@button.kw color="primary" type="submit">
${msg("doSubmit")}
</@button.kw>
</#if>
</@buttonGroup.kw>
</@form.kw>
</#if>
</@layout.registrationLayout>

View file

@ -1,10 +1,14 @@
<#import "template.ftl" as layout>
<#import "components/provider.ftl" as provider>
<#import "components/button/primary.ftl" as buttonPrimary>
<#import "components/checkbox/primary.ftl" as checkboxPrimary>
<#import "components/input/primary.ftl" as inputPrimary>
<#import "components/label/username.ftl" as labelUsername>
<#import "components/link/primary.ftl" as linkPrimary>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/checkbox.ftl" as checkbox>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<#import "components/atoms/link.ftl" as link>
<#import "components/molecules/identity-provider.ftl" as identityProvider>
<#import "features/labels/username.ftl" as usernameLabel>
<#assign usernameLabel><@usernameLabel.kw /></#assign>
<@layout.registrationLayout
displayInfo=realm.password && realm.registrationAllowed && !registrationDisabled??
@ -16,9 +20,8 @@
${msg("loginAccountTitle")}
<#elseif section="form">
<#if realm.password>
<form
action="${url.loginAction}"
class="m-0 space-y-4"
<@form.kw
action=url.loginAction
method="post"
onsubmit="login.disabled = true; return true;"
>
@ -27,58 +30,56 @@
type="hidden"
value="<#if auth.selectedCredential?has_content>${auth.selectedCredential}</#if>"
>
<div>
<@inputPrimary.kw
autocomplete=realm.loginWithEmailAllowed?string("email", "username")
autofocus=true
disabled=usernameEditDisabled??
invalid=["username", "password"]
name="username"
type="text"
value=(login.username)!''
>
<@labelUsername.kw />
</@inputPrimary.kw>
</div>
<div>
<@inputPrimary.kw
invalid=["username", "password"]
message=false
name="password"
type="password"
>
${msg("password")}
</@inputPrimary.kw>
</div>
<div class="flex items-center justify-between">
<#if realm.rememberMe && !usernameEditDisabled??>
<@checkboxPrimary.kw checked=login.rememberMe?? name="rememberMe">
${msg("rememberMe")}
</@checkboxPrimary.kw>
</#if>
<#if realm.resetPasswordAllowed>
<@linkPrimary.kw href=url.loginResetCredentialsUrl>
<span class="text-sm">${msg("doForgotPassword")}</span>
</@linkPrimary.kw>
</#if>
</div>
<div class="pt-4">
<@buttonPrimary.kw name="login" type="submit">
<@input.kw
autocomplete=realm.loginWithEmailAllowed?string("email", "username")
autofocus=true
disabled=usernameEditDisabled??
invalid=messagesPerField.existsError("username", "password")
label=usernameLabel
message=kcSanitize(messagesPerField.getFirstError("username", "password"))
name="username"
type="text"
value=(login.username)!''
/>
<@input.kw
invalid=messagesPerField.existsError("username", "password")
label=msg("password")
name="password"
type="password"
/>
<#if realm.rememberMe && !usernameEditDisabled?? || realm.resetPasswordAllowed>
<div class="flex items-center justify-between">
<#if realm.rememberMe && !usernameEditDisabled??>
<@checkbox.kw
checked=login.rememberMe??
label=msg("rememberMe")
name="rememberMe"
/>
</#if>
<#if realm.resetPasswordAllowed>
<@link.kw color="primary" href=url.loginResetCredentialsUrl size="small">
${msg("doForgotPassword")}
</@link.kw>
</#if>
</div>
</#if>
<@buttonGroup.kw>
<@button.kw color="primary" name="login" type="submit">
${msg("doLogIn")}
</@buttonPrimary.kw>
</div>
</form>
</@button.kw>
</@buttonGroup.kw>
</@form.kw>
</#if>
<#if realm.password && social.providers??>
<@provider.kw />
<@identityProvider.kw providers=social.providers />
</#if>
<#elseif section="info">
<#if realm.password && realm.registrationAllowed && !registrationDisabled??>
<div class="text-center">
${msg("noAccount")}
<@linkPrimary.kw href=url.registrationUrl>
<@link.kw color="primary" href=url.registrationUrl>
${msg("doRegister")}
</@linkPrimary.kw>
</@link.kw>
</div>
</#if>
</#if>

View file

@ -1,23 +1,24 @@
<#import "template.ftl" as layout>
<#import "components/button/primary.ftl" as buttonPrimary>
<#import "components/link/secondary.ftl" as linkSecondary>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/link.ftl" as link>
<@layout.registrationLayout; section>
<#if section = "header">
<#if section="header">
${msg("logoutConfirmTitle")}
<#elseif section = "form">
<#elseif section="form">
<p>${msg("logoutConfirmHeader")}</p>
<form action="${url.logoutConfirmAction}" class="m-0 space-y-4" method="post">
<@form.kw action=url.logoutConfirmAction method="post">
<input name="session_code" type="hidden" value="${logoutConfirm.code}">
<@buttonPrimary.kw name="confirmLogout" type="submit" value="${msg('doLogout')}">
<@button.kw color="primary" name="confirmLogout" type="submit" value=msg('doLogout')>
${msg("doLogout")}
</@buttonPrimary.kw>
</form>
</@button.kw>
</@form.kw>
<#if !logoutConfirm.skipLink>
<#if (client.baseUrl)?has_content>
<@linkSecondary.kw href=client.baseUrl>
<span class="text-sm">${kcSanitize(msg("backToApplication"))?no_esc}</span>
</@linkSecondary.kw>
<@link.kw color="secondary" href=client.baseUrl size="small">
${kcSanitize(msg("backToApplication"))?no_esc}
</@link.kw>
</#if>
</#if>
</#if>

View file

@ -1,7 +1,9 @@
<#import "template.ftl" as layout>
<#import "components/button/primary.ftl" as buttonPrimary>
<#import "components/input/primary.ftl" as inputPrimary>
<#import "components/link/secondary.ftl" as linkSecondary>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<#import "components/atoms/link.ftl" as link>
<@layout.registrationLayout
displayMessage=!messagesPerField.existsError("firstName", "lastName", "email", "username", "password", "password-confirm")
@ -11,90 +13,76 @@
<#if section="header">
${msg("registerTitle")}
<#elseif section="form">
<form action="${url.registrationAction}" class="m-0 space-y-4" method="post">
<div>
<@inputPrimary.kw
autocomplete="given-name"
autofocus=true
invalid=["firstName"]
name="firstName"
type="text"
value=(register.formData.firstName)!''
>
${msg("firstName")}
</@inputPrimary.kw>
</div>
<div>
<@inputPrimary.kw
autocomplete="family-name"
invalid=["lastName"]
name="lastName"
type="text"
value=(register.formData.lastName)!''
>
${msg("lastName")}
</@inputPrimary.kw>
</div>
<div>
<@inputPrimary.kw
autocomplete="email"
invalid=["email"]
name="email"
type="email"
value=(register.formData.email)!''
>
${msg("email")}
</@inputPrimary.kw>
</div>
<@form.kw action=url.registrationAction method="post">
<@input.kw
autocomplete="given-name"
autofocus=true
invalid=messagesPerField.existsError("firstName")
label=msg("firstName")
message=kcSanitize(messagesPerField.get("firstName"))
name="firstName"
type="text"
value=(register.formData.firstName)!''
/>
<@input.kw
autocomplete="family-name"
invalid=messagesPerField.existsError("lastName")
label=msg("lastName")
message=kcSanitize(messagesPerField.get("lastName"))
name="lastName"
type="text"
value=(register.formData.lastName)!''
/>
<@input.kw
autocomplete="email"
invalid=messagesPerField.existsError("email")
label=msg("email")
message=kcSanitize(messagesPerField.get("email"))
name="email"
type="email"
value=(register.formData.email)!''
/>
<#if !realm.registrationEmailAsUsername>
<div>
<@inputPrimary.kw
autocomplete="username"
invalid=["username"]
name="username"
type="text"
value=(register.formData.username)!''
>
${msg("username")}
</@inputPrimary.kw>
</div>
<@input.kw
autocomplete="username"
invalid=messagesPerField.existsError("username")
label=msg("username")
message=kcSanitize(messagesPerField.get("username"))
name="username"
type="text"
value=(register.formData.username)!''
/>
</#if>
<#if passwordRequired??>
<div>
<@inputPrimary.kw
autocomplete="new-password"
invalid=["password", "password-confirm"]
name="password"
type="password"
>
${msg("password")}
</@inputPrimary.kw>
</div>
<div>
<@inputPrimary.kw
autocomplete="new-password"
invalid=["password-confirm"]
name="password-confirm"
type="password"
>
${msg("passwordConfirm")}
</@inputPrimary.kw>
</div>
<@input.kw
autocomplete="new-password"
invalid=messagesPerField.existsError("password", "password-confirm")
label=msg("password")
message=kcSanitize(messagesPerField.getFirstError("password", "password-confirm"))
name="password"
type="password"
/>
<@input.kw
autocomplete="new-password"
invalid=messagesPerField.existsError("password-confirm")
label=msg("passwordConfirm")
message=kcSanitize(messagesPerField.get("password-confirm"))
name="password-confirm"
type="password"
/>
</#if>
<#if recaptchaRequired??>
<div>
<div class="g-recaptcha" data-sitekey="${recaptchaSiteKey}" data-size="compact"></div>
</div>
<div class="g-recaptcha" data-sitekey="${recaptchaSiteKey}" data-size="compact"></div>
</#if>
<div>
<@buttonPrimary.kw type="submit">
<@buttonGroup.kw>
<@button.kw color="primary" type="submit">
${msg("doRegister")}
</@buttonPrimary.kw>
</div>
</form>
</@button.kw>
</@buttonGroup.kw>
</@form.kw>
<#elseif section="nav">
<@linkSecondary.kw href=url.loginUrl>
<span class="text-sm">${kcSanitize(msg("backToLogin"))?no_esc}</span>
</@linkSecondary.kw>
<@link.kw color="secondary" href=url.loginUrl size="small">
${kcSanitize(msg("backToLogin"))?no_esc}
</@link.kw>
</#if>
</@layout.registrationLayout>

File diff suppressed because one or more lines are too long

View file

@ -1,67 +1,83 @@
<#import "components/document.ftl" as document>
<#import "components/layout/alerts.ftl" as alerts>
<#import "components/layout/another-way.ftl" as anotherWay>
<#import "components/layout/card.ftl" as card>
<#import "components/layout/card-footer.ftl" as cardFooter>
<#import "components/layout/card-header.ftl" as cardHeader>
<#import "components/layout/card-main.ftl" as cardMain>
<#import "components/layout/container.ftl" as container>
<#import "components/layout/locales.ftl" as locales>
<#import "components/layout/nav.ftl" as nav>
<#import "components/layout/required-fields.ftl" as requiredFields>
<#import "components/layout/title.ftl" as title>
<#import "components/layout/subtitle.ftl" as subtitle>
<#import "components/layout/username.ftl" as username>
<#import "document.ftl" as document>
<#import "components/atoms/alert.ftl" as alert>
<#import "components/atoms/body.ftl" as body>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/card.ftl" as card>
<#import "components/atoms/container.ftl" as container>
<#import "components/atoms/heading.ftl" as heading>
<#import "components/atoms/logo.ftl" as logo>
<#import "components/atoms/nav.ftl" as nav>
<#import "components/molecules/locale-provider.ftl" as localeProvider>
<#import "components/molecules/username.ftl" as username>
<#macro
registrationLayout
displayInfo=false
displayMessage=true
displayRequiredFields=false
script=""
showAnotherWayIfPresent=true
>
<#assign cardHeader>
<@logo.kw>
${kcSanitize(msg("loginTitleHtml", (realm.displayNameHtml!"")))?no_esc}
</@logo.kw>
<#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())>
<@heading.kw>
<#nested "header">
</@heading.kw>
<#else>
<#nested "show-username">
<@username.kw
linkHref=url.loginRestartFlowUrl
linkTitle=msg("restartLoginTooltip")
name=auth.attemptedUsername
/>
</#if>
</#assign>
<#assign cardContent>
<#if displayMessage && message?has_content && (message.type != "warning" || !isAppInitiatedAction??)>
<@alert.kw color=message.type>
${kcSanitize(message.summary)?no_esc}
</@alert.kw>
</#if>
<#nested "form">
<#if displayRequiredFields>
<p class="text-secondary-600 text-sm">
* ${msg("requiredFields")}
</p>
</#if>
<#if auth?has_content && auth.showTryAnotherWayLink() && showAnotherWayIfPresent>
<form action="${url.loginAction}" method="post">
<input name="tryAnotherWay" type="hidden" value="on" />
<@button.kw color="primary" type="submit">
${msg("doTryAnotherWay")}
</@button.kw>
</form>
</#if>
</#assign>
<#assign cardFooter>
<#if displayInfo>
<#nested "info">
</#if>
</#assign>
<html>
<head>
<@document.kw />
<@document.kw script=script />
</head>
<body>
<@body.kw>
<@container.kw>
<@card.kw>
<@cardHeader.kw>
<@title.kw />
<#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())>
<@subtitle.kw>
<#nested "header">
</@subtitle.kw>
<#else>
<@username.kw />
</#if>
</@cardHeader.kw>
<@cardMain.kw>
<#if displayMessage && message?has_content && (message.type != "warning" || !isAppInitiatedAction??)>
<@alerts.kw />
</#if>
<#nested "form">
<#if displayRequiredFields>
<@requiredFields.kw />
</#if>
<#if auth?has_content && auth.showTryAnotherWayLink() && showAnotherWayIfPresent>
<@anotherWay.kw />
</#if>
</@cardMain.kw>
<#if displayInfo>
<@cardFooter.kw>
<#nested "info">
</@cardFooter.kw>
</#if>
</@card.kw>
<@card.kw content=cardContent footer=cardFooter header=cardHeader />
<@nav.kw>
<#nested "nav">
<#if realm.internationalizationEnabled && locale.supported?size gt 1>
<@locales.kw />
<@localeProvider.kw currentLocale=locale.current locales=locale.supported />
</#if>
</@nav.kw>
</@container.kw>
</body>
</@body.kw>
</html>
</#macro>