terriajs
Version:
Geospatial data visualization platform.
163 lines (144 loc) • 4.78 kB
JSX
import createReactClass from "create-react-class";
import { observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import PropTypes from "prop-types";
import React from "react";
import { withTranslation } from "react-i18next";
import defined from "terriajs-cesium/Source/Core/defined";
import parseCustomMarkdownToReact from "../Custom/parseCustomMarkdownToReact";
import Loader from "../Loader";
import WarningBox from "../Preview/WarningBox";
import Styles from "./invoke-function.scss";
import ParameterEditor from "./ParameterEditor";
class FunctionViewModel {
constructor(catalogFunction) {
this.catalogFunction = catalogFunction;
this._parameters = {};
}
getParameter(parameter) {
let result = this._parameters[parameter.id];
if (!result || result.parameter !== parameter) {
result = this._parameters[parameter.id] = new ParameterViewModel(
parameter
);
}
return result;
}
}
class ParameterViewModel {
parameter;
userValue = undefined;
isValueValid = true;
wasEverBlurredWhileInvalid = false;
constructor(parameter) {
this.parameter = parameter;
}
}
const InvokeFunction = observer(
createReactClass({
displayName: "InvokeFunction",
propTypes: {
terria: PropTypes.object,
previewed: PropTypes.object,
viewState: PropTypes.object,
t: PropTypes.func.isRequired
},
/* eslint-disable-next-line camelcase */
UNSAFE_componentWillMount() {
this.parametersViewModel = new FunctionViewModel(this.props.previewed);
},
/* eslint-disable-next-line camelcase */
UNSAFE_componentWillUpdate(nextProps, nextState) {
if (nextProps.previewed !== this.parametersViewModel.catalogFunction) {
// Clear previous parameters view model, because this is a different catalog function.
this.parametersViewModel = new FunctionViewModel(nextProps.previewed);
}
},
submit() {
this.props.previewed.submitJob().catch((e) => {
this.props.terria.raiseErrorToUser(e);
});
runInAction(() => {
// Close modal window
this.props.viewState.explorerPanelIsVisible = false;
// mobile switch to nowvewing
this.props.viewState.switchMobileView(
this.props.viewState.mobileViewOptions.preview
);
});
},
getParams() {
// Key should include the previewed item identifier so that
// components are refreshed when different previewed items are
// displayed
return this.props.previewed.functionParameters.map((param, i) => (
<ParameterEditor
key={param.id + this.props.previewed.uniqueId}
parameter={param}
viewState={this.props.viewState}
previewed={this.props.previewed}
parameterViewModel={this.parametersViewModel.getParameter(param)}
/>
));
},
validateParameter(parameter) {
if (
!parameter.isValid ||
!this.parametersViewModel.getParameter(parameter).isValueValid
) {
// Editor says it's not valid, so it's not valid.
return false;
}
// Verify that required parameters have a value.
if (parameter.isRequired && !defined(parameter.value)) {
return false;
}
return true;
},
render() {
if (this.props.previewed.isLoading) {
return <Loader />;
}
let invalidParameters = false;
if (defined(this.props.previewed.parameters)) {
invalidParameters = !this.props.previewed.functionParameters.every(
this.validateParameter
);
}
const { t } = this.props;
return (
<div className={Styles.invokeFunction}>
<div className={Styles.content}>
<h3>{this.props.previewed.name}</h3>
<If condition={this.props.previewed.loadMetadataResult?.error}>
<WarningBox
error={this.props.previewed.loadMetadataResult?.error}
viewState={this.props.viewState}
/>
</If>
<div className={Styles.description}>
{parseCustomMarkdownToReact(this.props.previewed.description, {
catalogItem: this.props.previewed
})}
</div>
{this.getParams()}
</div>
<div className={Styles.footer}>
<button
type="button"
className={Styles.btn}
onClick={this.submit}
disabled={invalidParameters}
>
{t("analytics.runAnalysis")}
</button>
</div>
</div>
);
}
})
);
module.exports = withTranslation()(InvokeFunction);