@digital-blueprint/formalize-app
Version:
[GitHub Repository](https://github.com/digital-blueprint/formalize-app) | [npmjs package](https://www.npmjs.com/package/@digital-blueprint/formalize-app) | [Unpkg CDN](https://unpkg.com/browse/@digital-blueprint/formalize-app/) | [Formalize Bundle](https:
259 lines (226 loc) • 8.11 kB
JavaScript
import DBPLitElement from '@dbp-toolkit/common/dbp-lit-element.js';
import {ScopedElementsMixin} from '@dbp-toolkit/common';
import {css, html} from 'lit';
import {createInstance} from '../i18n.js';
import * as commonStyles from '@dbp-toolkit/common/styles.js';
import {classMap} from 'lit/directives/class-map.js';
import {getElementWebComponents} from '@dbp-toolkit/form-elements/src/utils.js';
import {getSelectorFixCSS} from '../styles.js';
import {
gatherFormDataFromElement,
validateRequiredFields,
} from '@dbp-toolkit/form-elements/src/utils.js';
export class BaseObject {
getUrlSlug() {
return 'url-slug';
}
getFormComponent() {
return BaseFormElement;
}
getFormIdentifier() {
return 'uuid';
}
}
export class BaseFormElement extends ScopedElementsMixin(DBPLitElement) {
constructor() {
super();
this._i18n = createInstance();
this.lang = this._i18n.language;
this.formData = {};
this.entryPointUrl = '';
this.auth = {};
this.saveButtonEnabled = true;
this.formIdentifier = '';
this.formUrlSlug = '';
this.formProperties = {};
this.userAllSubmissions = [];
this.allowedSubmissionStates = 4;
this.maxNumberOfSubmissionsPerUser = 10;
this.readOnly = false;
this.submissionId = '';
}
async validateAndSendSubmission(event) {
event.preventDefault();
const formElement = this.shadowRoot.querySelector('form');
// Validate the form before proceeding
const validationResult = await validateRequiredFields(formElement);
console.log('validateAndSendSubmission validationResult', validationResult);
if (!validationResult) {
this.scrollToFirstInvalidField(formElement);
return;
}
this.sendSubmission(event);
}
/**
* Scroll to the first invalid field in the form
* @param {HTMLFormElement} formElement
*/
scrollToFirstInvalidField(formElement) {
const elementWebComponents = getElementWebComponents(formElement);
for (const element of elementWebComponents) {
const invalidElement = element.shadowRoot.querySelector('.validation-errors');
if (invalidElement) {
const invalidFieldLabel = invalidElement.closest('fieldset').querySelector('label');
invalidFieldLabel.style.scrollMarginTop = '70px';
invalidFieldLabel.scrollIntoView({behavior: 'smooth'});
break;
}
}
}
/**
* Sends a submission event with the given form data.
* @param {object} event
*/
sendSubmission(event) {
this.saveButtonEnabled = false;
const formElement = this.shadowRoot.querySelector('form');
const data = {
formData: gatherFormDataFromElement(formElement),
};
console.log('sendSubmission data', data);
const customEvent = new CustomEvent('DbpFormalizeFormSubmission', {
detail: data,
bubbles: true,
composed: true,
});
this.dispatchEvent(customEvent);
}
/**
* Sends a draft submission event with the given form data.
* @param {object} event
*/
sendDraft(event) {
this.draftButtonEnabled = false;
const formElement = this.shadowRoot.querySelector('form');
const data = {
formData: gatherFormDataFromElement(formElement),
};
const customEvent = new CustomEvent('DbpFormalizeFormSaveDraft', {
bubbles: true,
composed: true,
detail: data,
});
this.dispatchEvent(customEvent);
}
/**
* Sends a delete submission event with the submission ID to delete.
* @param {object} event
*/
sendDeleteSubmission(event) {
if (!this.submissionId) {
return;
}
this.deleteButtonEnabled = false;
const data = {
submissionId: this.submissionId,
};
const customEvent = new CustomEvent('DbpFormalizeFormDeleteSubmission', {
bubbles: true,
composed: true,
detail: data,
});
this.dispatchEvent(customEvent);
}
/**
* Sends an accept submission event with the submission ID to accept.
* @param {object} event
*/
acceptSubmission(event) {
if (!this.submissionId) {
return;
}
this.acceptButtonEnabled = false;
const data = {
submissionId: this.submissionId,
};
const customEvent = new CustomEvent('DbpFormalizeFormAcceptSubmission', {
bubbles: true,
composed: true,
detail: data,
});
this.dispatchEvent(customEvent);
}
static get properties() {
return {
...super.properties,
lang: {type: String},
person: {type: Object},
additionalType: {type: String, attribute: 'additional-type'},
// For some reason, the attribute this.data was reset every time a new auth
// object was set by Keycloak, so we use this.formData instead
formData: {type: Object, attribute: false},
data: {type: Object},
auth: {type: Object},
entryPointUrl: {type: String, attribute: 'entry-point-url'},
formIdentifier: {type: String, attribute: 'form-identifier'},
formUrlSlug: {type: String, attribute: 'form-url-slug'},
readOnly: {type: Boolean, attribute: 'read-only'},
allowedSubmissionStates: {type: Number, attribute: 'allowed-submission-states'},
maxNumberOfSubmissionsPerUser: {type: String, attribute: 'max-number-of-submissions'},
formProperties: {type: Object},
userAllSubmissions: {type: Array},
saveButtonEnabled: {type: Boolean, attribute: false},
};
}
static get styles() {
// language=css
return css`
${commonStyles.getGeneralCSS(false)}
${commonStyles.getButtonCSS()}
${getSelectorFixCSS()}
.button-row {
margin-top: 1em;
text-align: right;
}
`;
}
resetForm(event) {
event.preventDefault();
this.saveButtonEnabled = true;
this.formData = {};
const customEvent = new CustomEvent('DbpFormalizeFormReset', {
bubbles: true,
composed: true,
});
this.dispatchEvent(customEvent);
}
/**
* Render the buttons needed for the form.
* @returns {import('lit').TemplateResult} HTML for the button row.
*/
getButtonRowHtml() {
const i18n = this._i18n;
return html`
<div class="button-row">
<button class="button is-secondary" type="button" =${this.resetForm} hidden>
${i18n.t('render-form.button-row.reset')}
</button>
<button
class="button is-primary"
type="submit"
?disabled=${!this.saveButtonEnabled}
=${this.validateAndSendSubmission}>
${i18n.t('render-form.button-row.submit')}
<dbp-mini-spinner
class="${classMap({hidden: this.saveButtonEnabled})}"></dbp-mini-spinner>
</button>
</div>
`;
}
update(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
switch (propName) {
case 'lang':
this._i18n.changeLanguage(this.lang);
break;
}
});
super.update(changedProperties);
}
render() {
console.log('-- Render BaseFormElement --');
return html`
<form>Please implement render() in your subclass! ${this.getButtonRowHtml()}</form>
`;
}
}