|
@@ -0,0 +1,140 @@
|
|
|
+/**
|
|
|
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
|
|
+ *
|
|
|
+ * @author David Sehnal <david.sehnal@gmail.com>
|
|
|
+ */
|
|
|
+
|
|
|
+import * as React from 'react';
|
|
|
+import { Transform, State, Transformer } from 'mol-state';
|
|
|
+import { StateAction } from 'mol-state/action';
|
|
|
+import { PluginCommands } from 'mol-plugin/command';
|
|
|
+import { PluginComponent } from './base';
|
|
|
+import { ParameterControls, createChangeSubject, ParamChanges } from './controls/parameters';
|
|
|
+import { Subject } from 'rxjs';
|
|
|
+import { shallowEqual } from 'mol-util/object';
|
|
|
+
|
|
|
+export { ActionContol }
|
|
|
+
|
|
|
+namespace ActionContol {
|
|
|
+ export interface Props {
|
|
|
+ nodeRef: Transform.Ref,
|
|
|
+ state: State,
|
|
|
+ action?: StateAction
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class ActionContol extends PluginComponent<ActionContol.Props, { params: any, initialParams: any, error?: string, busy: boolean, canApply: boolean }> {
|
|
|
+ private changes: ParamChanges;
|
|
|
+ private busy: Subject<boolean>;
|
|
|
+
|
|
|
+ cell = this.props.state.cells.get(this.props.nodeRef)!;
|
|
|
+ parentCell = (this.cell.sourceRef && this.props.state.cells.get(this.cell.sourceRef)) || void 0;
|
|
|
+
|
|
|
+ action: StateAction | Transformer = !this.props.action ? this.cell.transform.transformer : this.props.action
|
|
|
+ isUpdate = !this.props.action
|
|
|
+
|
|
|
+ getDefaultParams() {
|
|
|
+ if (this.isUpdate) {
|
|
|
+ return this.cell.transform.params;
|
|
|
+ } else {
|
|
|
+ const p = this.action.definition.params;
|
|
|
+ if (!p || !p.default) return {};
|
|
|
+ const obj = this.cell;
|
|
|
+ if (!obj.obj) return {};
|
|
|
+ return p.default(obj.obj, this.plugin);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ getParamDefinitions() {
|
|
|
+ if (this.isUpdate) {
|
|
|
+ const cell = this.cell;
|
|
|
+ const def = cell.transform.transformer.definition;
|
|
|
+
|
|
|
+ if (!cell.sourceRef || !def.params || !def.params.controls) return { };
|
|
|
+ const src = this.parentCell;
|
|
|
+ if (!src || !src.obj) return { };
|
|
|
+
|
|
|
+ return def.params.controls(src.obj, this.plugin);
|
|
|
+ } else {
|
|
|
+ const p = this.action.definition.params;
|
|
|
+ if (!p || !p.controls) return {};
|
|
|
+ const cell = this.cell;
|
|
|
+ if (!cell.obj) return {};
|
|
|
+ return p.controls(cell.obj, this.plugin);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ defaultState() {
|
|
|
+ const params = this.getDefaultParams();
|
|
|
+ return { error: void 0, params, initialParams: params, busy: false, canApply: !this.isUpdate };
|
|
|
+ }
|
|
|
+
|
|
|
+ apply = async () => {
|
|
|
+ this.setState({ busy: true, initialParams: this.state.params, canApply: !this.isUpdate });
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (Transformer.is(this.action)) {
|
|
|
+ await this.plugin.updateTransform(this.props.state, this.props.nodeRef, this.state.params);
|
|
|
+ } else {
|
|
|
+ await PluginCommands.State.ApplyAction.dispatch(this.plugin, {
|
|
|
+ state: this.props.state,
|
|
|
+ action: this.action.create(this.state.params),
|
|
|
+ ref: this.props.nodeRef
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ this.busy.next(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ validate(params: any) {
|
|
|
+ const def = this.isUpdate ? this.cell.transform.transformer.definition.params : this.action.definition.params;
|
|
|
+ if (!def || !def.validate) return;
|
|
|
+ const cell = this.cell;
|
|
|
+ const error = def.validate(params, this.isUpdate ? this.parentCell!.obj! : cell.obj!, this.plugin);
|
|
|
+ return error && error[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ init() {
|
|
|
+ this.changes = createChangeSubject();
|
|
|
+ this.subscribe(this.changes, ({ name, value }) => {
|
|
|
+ const params = { ...this.state.params, [name]: value };
|
|
|
+ const canApply = this.isUpdate ? !shallowEqual(params, this.state.initialParams) : true;
|
|
|
+ this.setState({ params, error: this.validate(params), canApply });
|
|
|
+ });
|
|
|
+
|
|
|
+ this.busy = new Subject();
|
|
|
+ this.subscribe(this.busy, busy => this.setState({ busy }));
|
|
|
+ }
|
|
|
+
|
|
|
+ onEnter = () => {
|
|
|
+ if (this.state.error) return;
|
|
|
+ this.apply();
|
|
|
+ }
|
|
|
+
|
|
|
+ refresh = () => {
|
|
|
+ this.setState({ params: this.state.initialParams, canApply: !this.isUpdate });
|
|
|
+ }
|
|
|
+
|
|
|
+ state = this.defaultState()
|
|
|
+
|
|
|
+ render() {
|
|
|
+ console.log('render', this.props.nodeRef, this.action.id);
|
|
|
+ const cell = this.cell;
|
|
|
+ if (cell.status !== 'ok' || (this.isUpdate && cell.transform.ref === Transform.RootRef)) return null;
|
|
|
+
|
|
|
+ const action = this.action;
|
|
|
+
|
|
|
+ return <div>
|
|
|
+ <div style={{ borderBottom: '1px solid #999', marginBottom: '5px' }}><h3>{(action.definition.display && action.definition.display.name) || action.id}</h3></div>
|
|
|
+
|
|
|
+ <ParameterControls params={this.getParamDefinitions()} values={this.state.params} changes={this.changes} onEnter={this.onEnter} isEnabled={!this.state.busy} />
|
|
|
+
|
|
|
+ <div style={{ textAlign: 'right' }}>
|
|
|
+ <span style={{ color: 'red' }}>{this.state.error}</span>
|
|
|
+ <button onClick={this.apply} disabled={!this.state.canApply || !!this.state.error || this.state.busy}>{this.isUpdate ? 'Update' : 'Create'}</button>
|
|
|
+ <button title='Refresh Params' onClick={this.refresh} disabled={this.state.busy}>↻</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+}
|