diff --git a/src/mol-plugin-ui/controls/action-menu.tsx b/src/mol-plugin-ui/controls/action-menu.tsx index bc223a0debb329857133837a6d9f9735cd9765e8..d3d703750bb1ca6e39aa0f47ada8f0890cd0221d 100644 --- a/src/mol-plugin-ui/controls/action-menu.tsx +++ b/src/mol-plugin-ui/controls/action-menu.tsx @@ -48,14 +48,16 @@ export namespace ActionMenu { items: ActionMenu.Spec, header?: string, label?: string, - current?: ActionMenu.Item, + current?: Item, onSelect: (value: any) => void } - export class Toggle extends React.PureComponent<ToggleProps, { isSelected: boolean }> { + type ToggleState = { current?: Item, isSelected: boolean } + + export class Toggle extends React.PureComponent<ToggleProps, ToggleState> { private sub: Subscription | undefined = void 0; - state = { isSelected: false }; + state = { isSelected: false, current: this.props.current }; componentDidMount() { this.sub = this.props.menu.commands.subscribe(command => { @@ -85,6 +87,11 @@ export namespace ActionMenu { this.props.menu.toggle(this.props); } + static getDerivedStateFromProps(props: ToggleProps, state: ToggleState) { + if (props.current === state.current) return null; + return { isSelected: false, current: props.current }; + } + render() { const props = this.props; const label = props.label || props.header; @@ -95,7 +102,9 @@ export namespace ActionMenu { } } - export class Options extends React.PureComponent<{ menu: ActionMenu }, { command: Command, isVisible: boolean }> { + type OptionsProps = { menu: ActionMenu, header?: string, items?: Spec, current?: Item | undefined } + + export class Options extends React.PureComponent<OptionsProps, { command: Command, isVisible: boolean }> { private sub: Subscription | undefined = void 0; state = { isVisible: false, command: HideCmd }; @@ -127,15 +136,21 @@ export namespace ActionMenu { } render() { - if (!this.state.isVisible || this.state.command.type !== 'toggle') return null; + const cmd = this.state.command; + if (!this.state.isVisible || cmd.type !== 'toggle') return null; + + if (this.props.items) { + if (cmd.items !== this.props.items || cmd.current !== this.props.current) return null; + } + return <div className='msp-action-menu-options' style={{ marginTop: '1px' }}> - {this.state.command.header && <div className='msp-control-group-header' style={{ position: 'relative' }}> + {cmd.header && <div className='msp-control-group-header' style={{ position: 'relative' }}> <button className='msp-btn msp-btn-block' onClick={this.hide}> <Icon name='off' style={{ position: 'absolute', right: '2px', top: 0 }} /> - <b>{this.state.command.header}</b> + <b>{cmd.header}</b> </button> </div>} - <Section menu={this.props.menu} items={this.state.command.items} onSelect={this.onSelect} current={this.state.command.current} /> + <Section menu={this.props.menu} items={cmd.items} onSelect={this.onSelect} current={cmd.current} /> </div> } } diff --git a/src/mol-plugin-ui/controls/parameters.tsx b/src/mol-plugin-ui/controls/parameters.tsx index 2576f59e727f1a7fc59e0b1f69c53b020103fff4..662ad304e1de0b73ffbb2bb006a9821906725e5d 100644 --- a/src/mol-plugin-ui/controls/parameters.tsx +++ b/src/mol-plugin-ui/controls/parameters.tsx @@ -71,7 +71,6 @@ export class ParameterMappingControl<S, T> extends PluginUIComponent<{ mapping: } } - function controlFor(param: PD.Any): ParamControl | undefined { switch (param.type) { case 'value': return void 0; @@ -128,6 +127,43 @@ export interface ParamProps<P extends PD.Base<any> = PD.Base<any>> { } export type ParamControl = React.ComponentClass<ParamProps<any>> +function renderSimple(options: { props: ParamProps<any>, state: { isExpanded: boolean }, control: JSX.Element, addOn: JSX.Element | null, toggleHelp: () => void }) { + const { props, state, control, toggleHelp, addOn } = options; + + const _className = ['msp-control-row']; + if (props.param.shortLabel) _className.push('msp-control-label-short') + if (props.param.twoColumns) _className.push('msp-control-col-2') + const className = _className.join(' '); + + const label = props.param.label || camelCaseToWords(props.name); + const help = props.param.help + ? props.param.help(props.value) + : { description: props.param.description, legend: props.param.legend } + const desc = props.param.description; + const hasHelp = help.description || help.legend + return <> + <div className={className}> + <span title={desc}> + {label} + {hasHelp && + <button className='msp-help msp-btn-link msp-btn-icon msp-control-group-expander' onClick={toggleHelp} + title={desc || `${state.isExpanded ? 'Hide' : 'Show'} help`} + style={{ background: 'transparent', textAlign: 'left', padding: '0' }}> + <span className={`msp-icon msp-icon-help-circle-${state.isExpanded ? 'collapse' : 'expand'}`} /> + </button> + } + </span> + <div> + {control} + </div> + </div> + {hasHelp && state.isExpanded && <div className='msp-control-offset'> + <ParamHelp legend={help.legend} description={help.description} /> + </div>} + {addOn} + </>; +} + export abstract class SimpleParam<P extends PD.Any> extends React.PureComponent<ParamProps<P>, { isExpanded: boolean }> { state = { isExpanded: false }; @@ -138,44 +174,16 @@ export abstract class SimpleParam<P extends PD.Any> extends React.PureComponent< abstract renderControl(): JSX.Element; renderAddOn(): JSX.Element | null { return null; } - private get className() { - const className = ['msp-control-row']; - if (this.props.param.shortLabel) className.push('msp-control-label-short') - if (this.props.param.twoColumns) className.push('msp-control-col-2') - return className.join(' ') - } - - - toggleExpanded = () => this.setState({ isExpanded: !this.state.isExpanded }); + toggleHelp = () => this.setState({ isExpanded: !this.state.isExpanded }); render() { - const label = this.props.param.label || camelCaseToWords(this.props.name); - const help = this.props.param.help - ? this.props.param.help(this.props.value) - : { description: this.props.param.description, legend: this.props.param.legend } - const desc = this.props.param.description; - const hasHelp = help.description || help.legend - return <> - <div className={this.className}> - <span title={desc}> - {label} - {hasHelp && - <button className='msp-help msp-btn-link msp-btn-icon msp-control-group-expander' onClick={this.toggleExpanded} - title={desc || `${this.state.isExpanded ? 'Hide' : 'Show'} help`} - style={{ background: 'transparent', textAlign: 'left', padding: '0' }}> - <span className={`msp-icon msp-icon-help-circle-${this.state.isExpanded ? 'collapse' : 'expand'}`} /> - </button> - } - </span> - <div> - {this.renderControl()} - </div> - </div> - {hasHelp && this.state.isExpanded && <div className='msp-control-offset'> - <ParamHelp legend={help.legend} description={help.description} /> - </div>} - {this.renderAddOn()} - </>; + return renderSimple({ + props: this.props, + state: this.state, + control: this.renderControl(), + toggleHelp: this.toggleHelp, + addOn: this.renderAddOn() + }); } } @@ -340,7 +348,10 @@ export class SelectControl extends SimpleParam<PD.Select<string | number>> { } renderAddOn() { - return <ActionMenu.Options menu={this.menu} />; + const items = this.items(this.props.param); + const current = ActionMenu.findCurrent(items, this.props.value); + + return <ActionMenu.Options menu={this.menu} items={items} current={current} />; } }