user profile pf5 (#27503)

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
This commit is contained in:
Erik Jan de Wit 2024-03-05 12:25:29 +01:00 committed by GitHub
parent 3f3375bc83
commit e7059f97b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 150 additions and 53 deletions

View file

@ -99,8 +99,8 @@
<script type="module" src="${url.resourcesPath}/js/passwordVisibility.js"></script>
<#elseif section = "info" >
<#if realm.password && realm.registrationAllowed && !registrationDisabled??>
<div id="kc-registration-container">
<div id="kc-registration">
<div id="kc-registration-container" class="pf-v5-c-login__main-footer-band">
<div id="kc-registration" class="pf-v5-c-login__main-footer-band-item">
<span>${msg("noAccount")} <a tabindex="6"
href="${url.registrationUrl}">${msg("doRegister")}</a></span>
</div>

View file

@ -84,17 +84,17 @@
</div>
</#if>
<div class="${properties.kcFormGroupClass!}">
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doRegister")}"/>
</div>
<div class="${properties.kcFormGroupClass!} pf-v5-c-login__main-footer-band">
<div id="kc-form-options" class="${properties.kcFormOptionsClass!} pf-v5-c-login__main-footer-band-item">
<div class="${properties.kcFormOptionsWrapperClass!}">
<span><a href="${url.loginUrl}">${kcSanitize(msg("backToLogin"))?no_esc}</a></span>
</div>
</div>
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doRegister")}"/>
</div>
</div>
</form>
<script type="module" src="${url.resourcesPath}/js/passwordVisibility.js"></script>
</#if>

View file

@ -0,0 +1,72 @@
// @ts-check
/**
* @typedef {Object} AnnotationDescriptor
* @property {string} name - The name of the field to register (e.g. `numberFormat`).
* @property {(element: HTMLElement) => (() => void) | void} onAdd - The function to call when a new element is added to the DOM.
*/
const observer = new MutationObserver(onMutate);
observer.observe(document.body, { childList: true, subtree: true });
/** @type {AnnotationDescriptor[]} */
const descriptors = [];
/** @type {WeakMap<HTMLElement, () => void>} */
const cleanupFunctions = new WeakMap();
/**
* @param {AnnotationDescriptor} descriptor
*/
export function registerElementAnnotatedBy(descriptor) {
descriptors.push(descriptor);
document.querySelectorAll(`[data-${descriptor.name}]`).forEach((element) => {
if (element instanceof HTMLElement) {
handleNewElement(element, descriptor);
}
});
}
/**
* @type {MutationCallback}
*/
function onMutate(mutations) {
const removedNodes = mutations.flatMap((mutation) => Array.from(mutation.removedNodes));
for (const node of removedNodes) {
if (!(node instanceof HTMLElement)) {
continue;
}
const handleRemovedElement = cleanupFunctions.get(node);
if (handleRemovedElement) {
handleRemovedElement();
}
cleanupFunctions.delete(node);
}
const addedNodes = mutations.flatMap((mutation) => Array.from(mutation.addedNodes));
for (const descriptor of descriptors) {
for (const node of addedNodes) {
const input = node.querySelector('input');
if (input.hasAttribute(`data-${descriptor.name}`)) {
handleNewElement(input, descriptor);
}
}
}
}
/**
* @param {HTMLElement} element
* @param {AnnotationDescriptor} descriptor
*/
function handleNewElement(element, descriptor) {
const cleanup = descriptor.onAdd(element);
if (cleanup) {
cleanupFunctions.set(element, cleanup);
}
}

View file

@ -29,36 +29,61 @@
</#if>
<#nested "beforeField" attribute>
<div class="${properties.kcFormGroupClass!}">
<label for="${attribute.name}" class="${properties.kcLabelClass!}">
<span class="pf-v5-c-form__label-text">
${advancedMsg(attribute.displayName!'')}
<#if attribute.required>
<span class="pf-v5-c-form__label-required" aria-hidden="true">&#42;</span>
<div class="${properties.kcFormGroupClass!}" x-data="{
values: [{ value: '${(attribute.value!'')}' }],
kcMultivalued: ${attribute.html5DataAnnotations?keys?seq_contains('kcMultivalued')?string('true', 'false')}
}"
>
<label for="${attribute.name}" class="${properties.kcLabelClass!}">
<span class="pf-v5-c-form__label-text">
${advancedMsg(attribute.displayName!'')}
<#if attribute.required>
<span class="pf-v5-c-form__label-required" aria-hidden="true">&#42;</span>
</#if>
</span>
</label>
<template x-for="(item, index) in values">
<div :class="kcMultivalued ? 'pf-v5-c-input-group' : ''">
<div :class="kcMultivalued ? 'pf-v5-c-input-group__item pf-m-fill' : ''">
<span class="${properties.kcInputClass!}" >
<#if attribute.annotations.inputHelperTextBefore??>
<div class="${properties.kcInputHelperTextBeforeClass!}" id="form-help-text-before-${attribute.name}" aria-live="polite">${kcSanitize(advancedMsg(attribute.annotations.inputHelperTextBefore))?no_esc}</div>
</#if>
<@inputFieldByType attribute=attribute/>
<#if messagesPerField.existsError('${attribute.name}')>
<span id="input-error-${attribute.name}" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
${kcSanitize(messagesPerField.get('${attribute.name}'))?no_esc}
</span>
</#if>
<#if attribute.annotations.inputHelperTextAfter??>
<div class="${properties.kcInputHelperTextAfterClass!}" id="form-help-text-after-${attribute.name}" aria-live="polite">${kcSanitize(advancedMsg(attribute.annotations.inputHelperTextAfter))?no_esc}</div>
</#if>
</span>
</label>
<span class="${properties.kcInputClass!}">
<#if attribute.annotations.inputHelperTextBefore??>
<div class="${properties.kcInputHelperTextBeforeClass!}" id="form-help-text-before-${attribute.name}" aria-live="polite">${kcSanitize(advancedMsg(attribute.annotations.inputHelperTextBefore))?no_esc}</div>
</#if>
<@inputFieldByType attribute=attribute/>
<#if messagesPerField.existsError('${attribute.name}')>
<span id="input-error-${attribute.name}" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
${kcSanitize(messagesPerField.get('${attribute.name}'))?no_esc}
</span>
</#if>
<#if attribute.annotations.inputHelperTextAfter??>
<div class="${properties.kcInputHelperTextAfterClass!}" id="form-help-text-after-${attribute.name}" aria-live="polite">${kcSanitize(advancedMsg(attribute.annotations.inputHelperTextAfter))?no_esc}</div>
</#if>
</div>
<div class="pf-v5-c-input-group__item" x-show="kcMultivalued">
<button
class="pf-v5-c-button pf-m-control"
type="button"
:id="$id('add-name-${attribute.name}')"
x-bind:disabled="index == 0 && values.length == 1"
x-on:click="values.splice(index, 1); $dispatch('bind')"
>
<svg fill="currentColor" height="1em" width="1em" viewBox="0 0 512 512" aria-hidden="true" role="img" style="vertical-align: -0.125em;"><path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zM124 296c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h264c6.6 0 12 5.4 12 12v56c0 6.6-5.4 12-12 12H124z"></path></svg>
</button>
</div>
</div>
</span>
</template>
<button type="button" class="pf-v5-c-button pf-m-link" x-show="kcMultivalued" x-on:click="values.push({ value: '' }); $dispatch('bind')">
<svg fill="currentColor" height="1em" width="1em" viewBox="0 0 512 512" aria-hidden="true" role="img" style="vertical-align: -0.125em;"><path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm144 276c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92h-92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"></path></svg>
Add ${advancedMsg(attribute.displayName!'')}
</button>
</div>
<#nested "afterField" attribute>
</#list>
<#list profile.html5DataAnnotations?keys as key>
<script type="module" src="${url.resourcesPath}/js/${key}.js"></script>
</#list>
<script type="module" src="${url.resourcesPath}/js/${key}.js"></script>
</#list>
</#macro>
<#macro inputFieldByType attribute>
@ -80,7 +105,7 @@
</#macro>
<#macro inputTag attribute>
<input type="<@inputTagType attribute=attribute/>" id="${attribute.name}" name="${attribute.name}" value="${(attribute.value!'')}" class="${properties.kcInputClass!}"
<input type="<@inputTagType attribute=attribute/>" :id="$id('name-${attribute.name}')" name="${attribute.name}" class="${properties.kcInputClass!}"
aria-invalid="<#if messagesPerField.existsError('${attribute.name}')>true</#if>"
<#if attribute.readOnly>disabled</#if>
<#if attribute.autocomplete??>autocomplete="${attribute.autocomplete}"</#if>
@ -94,7 +119,7 @@
<#if attribute.annotations.inputTypeStep??>step="${attribute.annotations.inputTypeStep}"</#if>
<#if attribute.annotations.inputTypeStep??>step="${attribute.annotations.inputTypeStep}"</#if>
<#list attribute.html5DataAnnotations as key, value>
data-${key}="${value}"
data-${key}="${value}"
</#list>
/>
</#macro>
@ -138,13 +163,13 @@
<#assign options=attribute.validators[attribute.annotations.inputOptionsFromValidation].options>
<#elseif attribute.validators.options?? && attribute.validators.options.options??>
<#assign options=attribute.validators.options.options>
<#else>
<#assign options=[]>
</#if>
<#if options??>
<#list options as option>
<#list options as option>
<option value="${option}" <#if attribute.values?seq_contains(option)>selected</#if>><@selectOptionLabelText attribute=attribute option=option/></option>
</#list>
</#if>
</#list>
</select>
</#macro>
@ -162,23 +187,23 @@
</#if>
<#if attribute.annotations.inputOptionsFromValidation?? && attribute.validators[attribute.annotations.inputOptionsFromValidation]?? && attribute.validators[attribute.annotations.inputOptionsFromValidation].options??>
<#assign options=attribute.validators[attribute.annotations.inputOptionsFromValidation].options>
<#elseif attribute.validators.options?? && attribute.validators.options.options??>
<#assign options=attribute.validators.options.options>
</#if>
<#assign options=attribute.validators[attribute.annotations.inputOptionsFromValidation].options>
<#elseif attribute.validators.options?? && attribute.validators.options.options??>
<#assign options=attribute.validators.options.options>
<#else>
<#assign options=[]>
</#if>
<#if options??>
<#list options as option>
<div class="${classDiv}">
<input type="${inputType}" id="${attribute.name}-${option}" name="${attribute.name}" value="${option}" class="${classInput}"
aria-invalid="<#if messagesPerField.existsError('${attribute.name}')>true</#if>"
<#if attribute.readOnly>disabled</#if>
<#if attribute.values?seq_contains(option)>checked</#if>
/>
<label for="${attribute.name}-${option}" class="${classLabel}<#if attribute.readOnly> ${properties.kcInputClassRadioCheckboxLabelDisabled!}</#if>"><@selectOptionLabelText attribute=attribute option=option/></label>
</div>
</#list>
</#if>
<#list options as option>
<div class="${classDiv}">
<input type="${inputType}" id="${attribute.name}-${option}" name="${attribute.name}" value="${option}" class="${classInput}"
aria-invalid="<#if messagesPerField.existsError('${attribute.name}')>true</#if>"
<#if attribute.readOnly>disabled</#if>
<#if attribute.values?seq_contains(option)>checked</#if>
/>
<label for="${attribute.name}-${option}" class="${classLabel}<#if attribute.readOnly> ${properties.kcInputClassRadioCheckboxLabelDisabled!}</#if>"><@selectOptionLabelText attribute=attribute option=option/></label>
</div>
</#list>
</#macro>
<#macro selectOptionLabelText attribute option>