common.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { StateObject, State, Transform, StateObjectCell, Transformer } from 'mol-state';
  7. import * as React from 'react';
  8. import { PurePluginComponent } from '../base';
  9. import { ParameterControls, ParamOnChange } from '../controls/parameters';
  10. import { StateAction } from 'mol-state/action';
  11. import { PluginContext } from 'mol-plugin/context';
  12. import { ParamDefinition as PD } from 'mol-util/param-definition';
  13. import { Subject } from 'rxjs';
  14. export { StateTransformParameters, TransformContolBase };
  15. class StateTransformParameters extends PurePluginComponent<StateTransformParameters.Props> {
  16. validate(params: any) {
  17. // TODO
  18. return void 0;
  19. }
  20. areInitial(params: any) {
  21. return PD.areEqual(this.props.info.params, params, this.props.info.initialValues);
  22. }
  23. onChange: ParamOnChange = ({ name, value }) => {
  24. const params = { ...this.props.params, [name]: value };
  25. this.props.events.onChange(params, this.areInitial(params), this.validate(params));
  26. };
  27. render() {
  28. return <ParameterControls params={this.props.info.params} values={this.props.params} onChange={this.onChange} onEnter={this.props.events.onEnter} isDisabled={this.props.isDisabled} />;
  29. }
  30. }
  31. namespace StateTransformParameters {
  32. export interface Props {
  33. info: {
  34. params: PD.Params,
  35. initialValues: any,
  36. source: StateObject,
  37. isEmpty: boolean
  38. },
  39. events: {
  40. onChange: (params: any, areInitial: boolean, errors?: string[]) => void,
  41. onEnter: () => void,
  42. }
  43. params: any,
  44. isDisabled?: boolean
  45. }
  46. export type Class = React.ComponentClass<Props>
  47. export function infoFromAction(plugin: PluginContext, state: State, action: StateAction, nodeRef: Transform.Ref): Props['info'] {
  48. const source = state.cells.get(nodeRef)!.obj!;
  49. const params = action.definition.params ? action.definition.params(source, plugin) : { };
  50. const initialValues = PD.getDefaultValues(params);
  51. return {
  52. source,
  53. initialValues,
  54. params,
  55. isEmpty: Object.keys(params).length === 0
  56. };
  57. }
  58. export function infoFromTransform(plugin: PluginContext, state: State, transform: Transform): Props['info'] {
  59. const cell = state.cells.get(transform.ref)!;
  60. const source: StateObjectCell | undefined = (cell.sourceRef && state.cells.get(cell.sourceRef)!) || void 0;
  61. const create = transform.transformer.definition.params;
  62. const params = create ? create((source && source.obj) as any, plugin) : { };
  63. return {
  64. source: (source && source.obj) as any,
  65. initialValues: transform.params,
  66. params,
  67. isEmpty: Object.keys(params).length === 0
  68. }
  69. }
  70. }
  71. namespace TransformContolBase {
  72. export interface State {
  73. params: any,
  74. error?: string,
  75. busy: boolean,
  76. isInitial: boolean,
  77. isCollapsed?: boolean
  78. }
  79. }
  80. abstract class TransformContolBase<P, S extends TransformContolBase.State> extends PurePluginComponent<P, S> {
  81. abstract applyAction(): Promise<void>;
  82. abstract getInfo(): StateTransformParameters.Props['info'];
  83. abstract getHeader(): Transformer.Definition['display'];
  84. abstract getHeaderFallback(): string;
  85. abstract canApply(): boolean;
  86. abstract applyText(): string;
  87. abstract state: S;
  88. private busy: Subject<boolean>;
  89. private onEnter = () => {
  90. if (this.state.error) return;
  91. this.apply();
  92. }
  93. events: StateTransformParameters.Props['events'] = {
  94. onEnter: this.onEnter,
  95. onChange: (params, isInitial, errors) => this.setState({ params, isInitial, error: errors && errors[0] })
  96. }
  97. apply = async () => {
  98. this.setState({ busy: true });
  99. try {
  100. await this.applyAction();
  101. } finally {
  102. this.busy.next(false);
  103. }
  104. }
  105. init() {
  106. this.busy = new Subject();
  107. this.subscribe(this.busy, busy => this.setState({ busy }));
  108. }
  109. refresh = () => {
  110. this.setState({ params: this.getInfo().initialValues, isInitial: true, error: void 0 });
  111. }
  112. setDefault = () => {
  113. const info = this.getInfo();
  114. const params = PD.getDefaultValues(info.params);
  115. this.setState({ params, isInitial: PD.areEqual(info.params, params, info.initialValues), error: void 0 });
  116. }
  117. toggleExpanded = () => {
  118. this.setState({ isCollapsed: !this.state.isCollapsed });
  119. }
  120. render() {
  121. const info = this.getInfo();
  122. if (info.isEmpty) return null;
  123. const display = this.getHeader();
  124. return <div className='msp-transform-wrapper'>
  125. <div className='msp-transform-header'>
  126. <button className='msp-btn msp-btn-block' onClick={this.toggleExpanded}>{(display && display.name) || this.getHeaderFallback()}</button>
  127. {!this.state.isCollapsed && <button className='msp-btn msp-btn-link msp-transform-default-params' onClick={this.setDefault} disabled={this.state.busy} style={{ float: 'right'}} title='Set default params'>↻</button>}
  128. </div>
  129. {!this.state.isCollapsed && <>
  130. <StateTransformParameters info={info} events={this.events} params={this.state.params} isDisabled={this.state.busy} />
  131. <div className='msp-transform-apply-wrap'>
  132. <button className='msp-btn msp-btn-block msp-transform-refresh msp-form-control' title='Refresh params' onClick={this.refresh} disabled={this.state.busy || this.state.isInitial}>
  133. ↶ Reset
  134. </button>
  135. <div className='msp-transform-apply'>
  136. <button className={`msp-btn msp-btn-block msp-btn-commit msp-btn-commit-${this.canApply() ? 'on' : 'off'}`} onClick={this.apply} disabled={!this.canApply()}>
  137. {this.applyText()}
  138. </button>
  139. </div>
  140. </div>
  141. </>}
  142. </div>
  143. }
  144. }