Skip to content
Snippets Groups Projects
Commit 509e633a authored by David Sehnal's avatar David Sehnal
Browse files

mol-plugin-ui: use ActionMenu in SelectControl

parent 17fac2b8
No related branches found
No related tags found
No related merge requests found
......@@ -45,7 +45,8 @@ export namespace ActionMenu {
menu: ActionMenu,
disabled?: boolean,
items: ActionMenu.Spec,
header: string,
header?: string,
label?: string,
current?: ActionMenu.Item,
onSelect: (value: any) => void
}
......@@ -78,11 +79,17 @@ export namespace ActionMenu {
hide = () => this.setState({ isSelected: false });
onClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.currentTarget.blur();
this.props.menu.toggle(this.props);
}
render() {
const props = this.props;
return <button onClick={() => props.menu.toggle(props)}
const label = props.label || props.header;
return <button onClick={this.onClick}
disabled={props.disabled} style={props.style} className={props.className}>
{this.state.isSelected ? <b>{props.header}</b> : props.header}
{this.state.isSelected ? <b>{label}</b> : label}
</button>;
}
}
......@@ -120,19 +127,19 @@ export namespace ActionMenu {
render() {
if (!this.state.isVisible || this.state.command.type !== 'toggle') return null;
return <div className='msp-action-menu-options'>
return <div className='msp-action-menu-options' style={{ marginTop: '1px' }}>
{this.state.command.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>
</button>
</div>}
<Section items={this.state.command.items} onSelect={this.onSelect} />
<Section menu={this.props.menu} items={this.state.command.items} onSelect={this.onSelect} current={this.state.command.current} />
</div>
}
}
class Section extends React.PureComponent<{ header?: string, items: Spec, onSelect: OnSelect }, { isExpanded: boolean }> {
class Section extends React.PureComponent<{ menu: ActionMenu, header?: string, items: Spec, onSelect: OnSelect, current: Item | undefined }, { isExpanded: boolean }> {
state = { isExpanded: false }
toggleExpanded = (e: React.MouseEvent<HTMLButtonElement>) => {
......@@ -141,9 +148,9 @@ export namespace ActionMenu {
}
render() {
const { header, items, onSelect } = this.props;
const { header, items, onSelect, current, menu } = this.props;
if (typeof items === 'string') return null;
if (isItem(items)) return <Action item={items} onSelect={onSelect} />
if (isItem(items)) return <Action menu={menu} item={items} onSelect={onSelect} current={current} />
return <div>
{header && <div className='msp-control-group-header' style={{ marginTop: '1px' }}>
<button className='msp-btn msp-btn-block' onClick={this.toggleExpanded}>
......@@ -154,19 +161,20 @@ export namespace ActionMenu {
<div className='msp-control-offset'>
{(!header || this.state.isExpanded) && items.map((x, i) => {
if (typeof x === 'string') return null;
if (isItem(x)) return <Action key={i} item={x} onSelect={onSelect} />
return <Section key={i} header={typeof x[0] === 'string' ? x[0] : void 0} items={x} onSelect={onSelect} />
if (isItem(x)) return <Action menu={menu} key={i} item={x} onSelect={onSelect} current={current} />
return <Section menu={menu} key={i} header={typeof x[0] === 'string' ? x[0] : void 0} items={x} onSelect={onSelect} current={current} />
})}
</div>
</div>;
}
}
const Action: React.FC<{ item: Item, onSelect: OnSelect }> = ({ item, onSelect }) => {
const Action: React.FC<{ menu: ActionMenu, item: Item, onSelect: OnSelect, current: Item | undefined }> = ({ menu, item, onSelect, current }) => {
const isCurrent = current === item;
return <div className='msp-control-row'>
<button onClick={() => onSelect(item)}>
<button onClick={isCurrent ? () => menu.hide() : () => onSelect(item)}>
{item.icon && <Icon name={item.icon} />}
{item.name}
{isCurrent ? <b>{item.name}</b> : item.name}
</button>
</div>;
}
......
......@@ -22,6 +22,7 @@ import { CombinedColorControl, ColorValueOption, ColorOptions } from './color';
import { getPrecision } from '../../mol-util/number';
import { ParamMapping } from '../../mol-util/param-mapping';
import { PluginContext } from '../../mol-plugin/context';
import { ActionMenu } from './action-menu';
export interface ParameterControlsProps<P extends PD.Params = PD.Params> {
params: P,
......@@ -135,6 +136,7 @@ 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'];
......@@ -143,6 +145,7 @@ export abstract class SimpleParam<P extends PD.Any> extends React.PureComponent<
return className.join(' ')
}
toggleExpanded = () => this.setState({ isExpanded: !this.state.isExpanded });
render() {
......@@ -171,6 +174,7 @@ export abstract class SimpleParam<P extends PD.Any> extends React.PureComponent<
{hasHelp && this.state.isExpanded && <div className='msp-control-offset'>
<ParamHelp legend={help.legend} description={help.description} />
</div>}
{this.renderAddOn()}
</>;
}
}
......@@ -319,20 +323,32 @@ export class PureSelectControl extends React.PureComponent<ParamProps<PD.Select
}
export class SelectControl extends SimpleParam<PD.Select<string | number>> {
onChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
if (typeof this.props.param.defaultValue === 'number') {
this.update(parseInt(e.target.value, 10));
} else {
this.update(e.target.value);
}
menu = new ActionMenu();
onSelect = (value: string) => {
this.update(value);
}
renderControl() {
const isInvalid = this.props.value !== void 0 && !this.props.param.options.some(e => e[0] === this.props.value);
return <select value={this.props.value !== void 0 ? this.props.value : this.props.param.defaultValue} onChange={this.onChange} disabled={this.props.isDisabled}>
{isInvalid && <option key={this.props.value} value={this.props.value}>{`[Invalid] ${this.props.value}`}</option>}
{this.props.param.options.map(([value, label]) => <option key={value} value={value}>{label}</option>)}
</select>;
const items: ActionMenu.Item[] = [];
let current: ActionMenu.Item | undefined = void 0;
if (isInvalid) {
current = ActionMenu.Item(`[Invalid] ${this.props.value}`, this.props.value);
items.push(current);
}
for (const [value, label] of this.props.param.options) {
const item = ActionMenu.Item(label, value);
items.push(item);
if (value === this.props.value) current = item;
}
return <ActionMenu.Toggle menu={this.menu} disabled={this.props.isDisabled}
onSelect={this.onSelect} items={items as ActionMenu.Spec} label={current?.name}
current={current} />;
}
renderAddOn() {
return <ActionMenu.Options menu={this.menu} />;
}
}
......
......@@ -210,7 +210,7 @@ export class StructureSelectionControls<P, S extends StructureSelectionControlsS
actionMenu = new ActionMenu();
controls = <div>
<div className='msp-control-row msp-button-row' style={{ marginBottom: '1px' }}>
<div className='msp-control-row msp-button-row'>
<ActionMenu.Toggle menu={this.actionMenu} items={this.queries} header='Select' onSelect={this.add} disabled={this.state.isDisabled} />
<ActionMenu.Toggle menu={this.actionMenu} items={this.queries} header='Deselect' onSelect={this.remove} disabled={this.state.isDisabled} />
<ActionMenu.Toggle menu={this.actionMenu} items={this.queries} header='Only' onSelect={this.only} disabled={this.state.isDisabled} />
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment