lion/packages/ui/components/button/LionButtonSubmit.js
2022-10-31 16:55:07 +01:00

109 lines
3.3 KiB
JavaScript

import { LionButtonReset } from './LionButtonReset.js';
/**
* @typedef {import('@lion/core').TemplateResult} TemplateResult
* @typedef {{lionButtons: Set<LionButtonSubmit>, helper:HTMLButtonElement, observer:MutationObserver}} HelperRegistration
*/
/** @type {WeakMap<HTMLFormElement, HelperRegistration>} */
const implicitHelperMap = new WeakMap();
function createImplicitSubmitHelperButton() {
const buttonEl = document.createElement('button');
buttonEl.tabIndex = -1;
buttonEl.type = 'submit';
buttonEl.setAttribute('aria-hidden', 'true');
// Make it sr-only
buttonEl.style.cssText = `
position: absolute;
top: 0;
left: 0;
clip: rect(0 0 0 0);
clip-path: inset(50%);
overflow: hidden;
white-space: nowrap;
height: 1px;
width: 1px;
padding: 0; /* reset default agent styles */
border: 0; /* reset default agent styles */
`;
return buttonEl;
}
/**
* Contains all the funcionaility of LionButton and LionButtonReset. On top of that it
* supports implicit form submission.
*
* Use when:
* - the Application Developer should be able to switch types between 'submit'|'reset'|'button'
* (this is similar to how native HTMLButtonElement works)
* - a submit button with native form support is needed
*/
export class LionButtonSubmit extends LionButtonReset {
/**
* @type {HTMLButtonElement|null}
* @protected
*/
get _nativeButtonNode() {
return implicitHelperMap.get(/** @type {HTMLFormElement} */ (this._form))?.helper || null;
}
constructor() {
super();
this.type = 'submit';
/** @type {HTMLButtonElement|null} */
this.__implicitSubmitHelperButton = null;
}
_setupSubmitAndResetHelperOnConnected() {
super._setupSubmitAndResetHelperOnConnected();
if (!this._form || this.type !== 'submit') {
return;
}
/** @type {HTMLFormElement} */
const form = this._form;
const registrationForCurForm = implicitHelperMap.get(this._form);
if (!registrationForCurForm) {
const buttonEl = createImplicitSubmitHelperButton();
const wrapperEl = document.createElement('div');
wrapperEl.appendChild(buttonEl);
implicitHelperMap.set(this._form, {
lionButtons: new Set(),
helper: buttonEl,
observer: new MutationObserver(() => {
form.appendChild(wrapperEl);
}),
});
form.appendChild(wrapperEl);
// Prevent that the just created button gets lost during rerender
implicitHelperMap.get(form)?.observer.observe(wrapperEl, { childList: true });
}
implicitHelperMap.get(form)?.lionButtons.add(this);
}
_teardownSubmitAndResetHelperOnDisconnected() {
super._teardownSubmitAndResetHelperOnDisconnected();
if (this._form) {
// If we are the last button to leave the form, clean up the
const registrationForCurForm = /** @type {HelperRegistration} */ (
implicitHelperMap.get(/** @type {HTMLFormElement} */ (this._form))
);
if (registrationForCurForm) {
registrationForCurForm.lionButtons.delete(this);
if (!registrationForCurForm.lionButtons.size) {
if (this._form.contains(registrationForCurForm.helper)) {
registrationForCurForm.helper.remove();
}
implicitHelperMap.get(this._form)?.observer.disconnect();
implicitHelperMap.delete(this._form);
}
}
}
}
}