user profile pf5 (#27503)
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
This commit is contained in:
parent
3f3375bc83
commit
e7059f97b7
4 changed files with 150 additions and 53 deletions
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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">*</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">*</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>
|
||||
|
|
Loading…
Reference in a new issue