src/components/_classes/componentModal/ComponentModal.js
import _ from 'lodash';
import { fastCloneDeep } from '../../../utils/utils';
export default class ComponentModal {
static render(component, data, topLevel) {
const children = component.renderTemplate('component', data, topLevel);
const isOpened = this;
return component.renderTemplate('componentModal', {
...data,
children,
isOpened
});
}
constructor(component, element, isOpened, currentValue) {
this.isOpened = isOpened;
this.component = component;
this.element = element;
this.currentValue = fastCloneDeep(currentValue);
this.dataLoaded = false;
this.init();
}
get refs() {
return this.component.refs;
}
init() {
this.openModalListener = this.openModalHandler.bind(this);
this.showDialogListener = (event) => {
if (this.isValueChanged() && !this.component.disabled) {
this.showDialog();
}
else {
this.closeModalHandler(event);
}
};
this.closeModalListener = this.closeModalHandler.bind(this);
this.saveModalListener = this.saveModalValueHandler.bind(this);
this.closeDialogListener = this.closeDialog.bind(this);
this.saveDialogListener = this.saveDialog.bind(this);
this.loadRefs();
}
setValue(value) {
if (this.dataLoaded) {
return;
}
this.currentValue = fastCloneDeep(value);
this.dataLoaded = true;
this.updateView();
}
setOpenModalElement(template) {
this.openModalTemplate = template;
this.component.setContent(this.refs.openModalWrapper, template);
this.loadRefs();
this.setEventListeners();
if (this.isOpened) {
this.refs.modalWrapper.classList.add('formio-dialog-disabled-animation');
this.openModal();
}
}
get templateRefs() {
return {
modalOverlay: 'single',
modalContents: 'single',
modalClose: 'single',
openModalWrapper: 'single',
openModal: 'single',
modalSave: 'single',
modalWrapper: 'single',
};
}
loadRefs() {
this.component.loadRefs(this.element, this.templateRefs);
}
removeEventListeners() {
this.component.removeEventListener(this.refs.openModal, 'click', this.openModalListener);
this.component.removeEventListener(this.refs.modalOverlay, 'click', this.refs.modalSave ? this.showDialogListener : this.saveModalListener);
this.component.removeEventListener(this.refs.modalClose, 'click', this.showDialogListener);
this.component.removeEventListener(this.refs.modalSave, 'click', this.saveModalListener);
}
setEventListeners() {
this.removeEventListeners();
this.component.addEventListener(this.refs.openModal, 'click', this.openModalListener);
this.component.addEventListener(this.refs.modalOverlay, 'click', this.refs.modalSave ? this.showDialogListener : this.saveModalListener);
this.component.addEventListener(this.refs.modalClose, 'click', this.showDialogListener);
this.component.addEventListener(this.refs.modalSave, 'click', this.saveModalListener);
}
isValueChanged() {
let componentValue = this.component.getValue();
let currentValue = this.currentValue;
//excluding metadata comparison for components that have it in dataValue (for ex. nested forms)
if (componentValue && componentValue.data && componentValue.metadata) {
componentValue = this.component.getValue().data;
currentValue = this.currentValue.data;
}
return !_.isEqual(componentValue, currentValue);
}
setOpenEventListener() {
this.component.removeEventListener(this.refs.openModal, 'click', this.openModalListener);
this.component.loadRefs(this.refs.openModalWrapper, {
'openModal': 'single',
});
this.component.addEventListener(this.refs.openModal, 'click', this.openModalListener);
}
openModalHandler(event) {
event.preventDefault();
this.openModal();
}
positionOverElement() {
// Position the modal just over the element on the page.
const elementOffset = this.element.getBoundingClientRect().top;
const modalHeight = this.refs.modalContents.getBoundingClientRect().height;
let modalTop = elementOffset - modalHeight - 10;
modalTop = modalTop > 0 ? modalTop : 10;
this.refs.modalWrapper.style.paddingTop = `${modalTop}px`;
}
openModal() {
this.isOpened = true;
this.refs.modalWrapper.classList.remove('component-rendering-hidden');
if (this.component.component.type === 'signature') {
// Position signature modals just above the signature button.
this.positionOverElement();
}
}
updateView() {
const template = _.isEqual(this.currentValue, this.component.defaultValue)
? this.openModalTemplate
: this.component.getModalPreviewTemplate();
this.component.setContent(this.refs.openModalWrapper, template);
this.setOpenEventListener();
}
closeModal() {
this.refs.modalWrapper.classList.remove('formio-dialog-disabled-animation');
this.refs.modalWrapper.classList.add('component-rendering-hidden');
this.isOpened = false;
this.updateView();
}
closeModalHandler(event) {
event.preventDefault();
if (!this.component.disabled) {
this.component.setValue(_.cloneDeep(this.currentValue), { resetValue: true });
}
this.closeModal();
}
showDialog() {
this.dialogElement = this.component.ce('div');
const dialogContent = `
<h3 ref="dialogHeader">${this.component.t('Do you want to clear changes?')}</h3>
<div style="display:flex; justify-content: flex-end;">
<button ref="dialogCancelButton" class="btn btn-secondary">${this.component.t('Cancel')}</button>
<button ref="dialogYesButton" class="btn btn-danger">${this.component.t('Yes, delete it')}</button>
</div>
`;
this.dialogElement.innerHTML = dialogContent;
this.dialogElement.refs = {};
this.component.loadRefs.call(this.dialogElement, this.dialogElement, {
dialogHeader: 'single',
dialogCancelButton: 'single',
dialogYesButton: 'single',
});
this.dialog = this.component.createModal(this.dialogElement);
this.component.addEventListener(this.dialogElement.refs.dialogYesButton, 'click', this.saveDialogListener);
this.component.addEventListener(this.dialogElement.refs.dialogCancelButton, 'click', this.closeDialogListener);
}
closeDialog(event) {
event.preventDefault();
this.dialog.close();
this.component.removeEventListener(this.dialogElement.refs.dialogYesButton, 'click', this.saveDialogListener);
this.component.removeEventListener(this.dialogElement.refs.dialogCancelButton, 'click', this.closeDialogListener);
}
saveDialog(event) {
this.closeDialog(event);
this.closeModalHandler(event);
}
saveModalValueHandler(event) {
event.preventDefault();
this.currentValue = fastCloneDeep(this.component.dataValue);
this.closeModal();
}
}