-
David Sehnal authoredDavid Sehnal authored
action.tsx 5.16 KiB
/**
* 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>
}
}