Mosaico upgraded to 0.17.5

Work started on confirmation dialogs displayed when one navigates from a page with unsaved changes
This commit is contained in:
Tomas Bures 2019-05-08 19:54:19 +02:00
parent 4f77272042
commit 48dcf2c701
399 changed files with 4032 additions and 77702 deletions

View file

@ -12,6 +12,7 @@ import {TreeSelectMode, TreeTable} from './tree';
import {Table, TableSelectMode} from './table';
import {Button} from "./bootstrap-components";
import {SketchPicker} from 'react-color';
import deepEqual from "fast-deep-equal";
import ACEEditorRaw from 'react-ace';
import 'brace/theme/github';
@ -49,11 +50,20 @@ export const FormStateOwnerContext = React.createContext(null);
const withFormStateOwner = createComponentMixin([{context: FormStateOwnerContext, propName: 'formStateOwner'}], [], (TargetClass, InnerClass) => {
InnerClass.prototype.getFormStateOwner = function() {
return this.props.formStateOwner;
}
};
return {};
});
export function withFormErrorHandlers(target, name, descriptor) {
const asyncFn = descriptor.value;
descriptor.value = async function(...args) {
await this.formHandleErrors(async () => await asyncFn.apply(this, args));
};
return descriptor;
}
@withComponentMixins([
withTranslation,
@ -61,6 +71,15 @@ const withFormStateOwner = createComponentMixin([{context: FormStateOwnerContext
withPageHelpers
])
class Form extends Component {
constructor(props) {
super(props);
this.beforeUnloadHandlers = {
handler: () => this.props.stateOwner.isFormChanged(),
handlerAsync: async () => await this.props.stateOwner.isFormChangedAsync()
};
}
static propTypes = {
stateOwner: PropTypes.object.isRequired,
onSubmitAsync: PropTypes.func,
@ -68,6 +87,14 @@ class Form extends Component {
noStatus: PropTypes.bool
}
componentDidMount() {
this.registerBeforeUnloadHandlers(this.beforeUnloadHandlers);
}
componentWillUnmount() {
this.deregisterBeforeUnloadHandlers(this.beforeUnloadHandlers);
}
@withAsyncErrorHandler
async onSubmit(evt) {
const t = this.props.t;
@ -77,7 +104,7 @@ class Form extends Component {
evt.preventDefault();
if (this.props.onSubmitAsync) {
await owner.formHandleChangedError(async () => await this.props.onSubmitAsync());
await this.props.onSubmitAsync();
}
}
@ -952,6 +979,7 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
isDisabled: false,
statusMessageText: '',
data: Immutable.Map(),
savedData: Immutable.Map(),
isServerValidationRunning: false
});
@ -1061,12 +1089,14 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
});
};
proto.getFormValuesFromEntity = function(entity, mutator) {
proto.getFormValuesFromEntity = function(entity) {
const settings = this.state.formSettings;
const data = Object.assign({}, entity);
data.originalHash = data.hash;
delete data.hash;
const mutator = settings.loadMutator;
if (mutator) {
mutator(data);
}
@ -1074,7 +1104,8 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
this.populateFormValues(data);
};
proto.getFormValuesFromURL = async function(url, mutator) {
proto.getFormValuesFromURL = async function(url) {
const settings = this.state.formSettings;
setTimeout(() => {
this.setState(previousState => {
if (previousState.formState.get('state') === FormState.Loading) {
@ -1092,6 +1123,7 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
data.originalHash = data.hash;
delete data.hash;
const mutator = settings.loadMutator;
if (mutator) {
const newData = mutator(data);
@ -1103,12 +1135,26 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
this.populateFormValues(data);
};
proto.validateAndSendFormValuesToURL = async function(method, url, mutator) {
proto.validateAndSendFormValuesToURL = async function(method, url) {
const settings = this.state.formSettings;
await this.waitForFormServerValidated();
if (this.isFormWithoutErrors()) {
if (settings.getPreSubmitUpdater) {
const preSubmitUpdater = await settings.getPreSubmitUpdater();
await new Promise((resolve, reject) => {
this.setState(previousState => ({
formState: previousState.formState.withMutations(mutState => {
mutState.update('data', stateData => stateData.withMutations(preSubmitUpdater));
})
}), resolve);
});
}
let data = this.getFormValues();
const mutator = settings.submitMutator;
if (mutator) {
const newData = mutator(data);
if (newData !== undefined) {
@ -1118,6 +1164,12 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
const response = await axios.method(method, getUrl(url), data);
await new Promise((resolve, reject) => {
this.setState(previousState => ({
formState: previousState.formState.set('savedData', previousState.formState.get('data'))
}), resolve);
});
return response.data || true;
} else {
@ -1140,6 +1192,8 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
}
}));
mutState.set('savedData', mutState.get('data'));
validateFormState(this, mutState);
})
}));
@ -1263,6 +1317,57 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
return this.state.formState.get('state') === FormState.Ready;
};
const _isFormChanged = self => {
const settings = self.state.formSettings;
const mutateData = data => {
if (settings.submitMutator) {
const newData = settings.submitMutator(data);
if (newData !== undefined) {
data = newData;
}
}
return data;
};
const currentData = mutateData(self.state.formState.get('data').map(attr => attr.get('value')).toJS());
const savedData = mutateData(self.state.formState.get('savedData').map(attr => attr.get('value')).toJS());
return !deepEqual(currentData, savedData);
};
proto.isFormChanged = function() {
const settings = this.state.formSettings;
if (settings.getPreSubmitUpdater) {
// getPreSubmitUpdater is an async function. We cannot do anything async here. So to be on the safe side,
// we simply assume that the form has been changed.
return true;
}
return _isFormChanged(this);
};
proto.isFormChangedAsync = async function() {
const settings = this.state.formSettings;
if (settings.getPreSubmitUpdater) {
const preSubmitUpdater = await settings.getPreSubmitUpdater();
await new Promise((resolve, reject) => {
this.setState(previousState => ({
formState: previousState.formState.withMutations(mutState => {
mutState.update('data', stateData => stateData.withMutations(preSubmitUpdater));
})
}), resolve);
});
}
return _isFormChanged(this);
};
proto.isFormValidationShown = function() {
return this.state.formState.get('isValidationShown');
};
@ -1341,7 +1446,7 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
return this.state.formState.get('isDisabled');
};
proto.formHandleChangedError = async function(fn) {
proto.formHandleErrors = async function(fn) {
const t = this.props.t;
try {
await fn();
@ -1386,6 +1491,23 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
return {};
});
function filterData(obj, allowedKeys) {
const result = {};
for (const key in obj) {
if (key === 'originalHash') {
result[key] = obj[key];
} else {
for (const allowedKey of allowedKeys) {
if ((typeof allowedKey === 'function' && allowedKey(key)) || allowedKey === key) {
result[key] = obj[key];
break;
}
}
}
}
return result;
}
export {
withForm,
@ -1407,5 +1529,6 @@ export {
TableSelect,
TableSelectMode,
ACEEditor,
FormSendMethod
FormSendMethod,
filterData
}