Browse Source

ui: add option to cycle through select options on click

Alexander Rose 5 years ago
parent
commit
9d45beea3b

+ 2 - 0
src/mol-plugin-ui/controls/common.tsx

@@ -314,6 +314,7 @@ export type ToggleButtonProps = {
     disabled?: boolean,
     label: string | JSX.Element,
     title?: string,
+    icon?: string,
     isSelected?: boolean,
     toggle: () => void
 }
@@ -329,6 +330,7 @@ export class ToggleButton extends React.PureComponent<ToggleButtonProps> {
         const label = props.label;
         return <button onClick={this.onClick} title={this.props.title}
             disabled={props.disabled} style={props.style} className={props.className}>
+            {this.props.icon ? <span className={`msp-icon msp-icon-${this.props.icon}`} /> : ''}
             {this.props.isSelected ? <b>{label}</b> : label}
         </button>;
     }

+ 17 - 3
src/mol-plugin-ui/controls/parameters.tsx

@@ -78,7 +78,7 @@ export class ParameterControls<P extends PD.Params> extends React.PureComponent<
 
         const essentials = this.renderPart(groups.essentials);
         const advanced = this.renderPart(groups.advanced);
-        
+
         if (essentials && advanced) {
             return <>
                 {essentials}
@@ -455,6 +455,13 @@ export class SelectControl extends React.PureComponent<ParamProps<PD.Select<stri
 
     toggle = () => this.setState({ showOptions: !this.state.showOptions });
 
+    cycle = () => {
+        const { options } = this.props.param
+        const current = options.findIndex(o => o[0] === this.props.value)
+        const next = current === options.length - 1 ? 0 : current + 1
+        this.props.onChange({ param: this.props.param, name: this.props.name, value: options[next][0] });
+    };
+
     items = memoizeLatest((param: PD.Select<any>) => ActionMenu.createItemsFromSelectParam(param));
 
     renderControl() {
@@ -466,8 +473,15 @@ export class SelectControl extends React.PureComponent<ParamProps<PD.Select<stri
                 ? `${ActionMenu.getFirstItem(items)?.label || ''} [Default]`
                 : `[Invalid] ${this.props.value}`;
 
-        return <ToggleButton disabled={this.props.isDisabled} style={{ textAlign: 'left', overflow: 'hidden', textOverflow: 'ellipsis' }}
-            label={label} title={label as string} toggle={this.toggle} isSelected={this.state.showOptions} />;
+        const toggle = this.props.param.cycle ? this.cycle : this.toggle
+        const textAlign = this.props.param.cycle ? 'center' : 'left'
+        const icon = this.props.param.cycle
+            ? (this.props.value === 'on' ? 'ok'
+                : this.props.value === 'off' ? 'off' : '')
+            : ''
+
+        return <ToggleButton disabled={this.props.isDisabled} style={{ textAlign, overflow: 'hidden', textOverflow: 'ellipsis' }}
+            label={label} title={label as string} icon={icon} toggle={toggle} isSelected={this.state.showOptions} />;
     }
 
     renderAddOn() {

+ 5 - 4
src/mol-util/param-definition.ts

@@ -74,9 +74,10 @@ export namespace ParamDefinition {
         type: 'select'
         /** array of (value, label) tuples */
         options: readonly (readonly [T, string] | readonly [T, string, string])[]
+        cycle?: boolean
     }
-    export function Select<T extends string | number>(defaultValue: T, options: readonly (readonly [T, string] | readonly [T, string, string])[], info?: Info): Select<T> {
-        return setInfo<Select<T>>({ type: 'select', defaultValue: checkDefaultKey(defaultValue, options), options }, info)
+    export function Select<T extends string | number>(defaultValue: T, options: readonly (readonly [T, string] | readonly [T, string, string])[], info?: Info & { cycle?: boolean }): Select<T> {
+        return setInfo<Select<T>>({ type: 'select', defaultValue: checkDefaultKey(defaultValue, options), options, cycle: info?.cycle }, info)
     }
 
     export interface ColorList<T extends string> extends Base<T> {
@@ -209,7 +210,7 @@ export namespace ParamDefinition {
         select: Select<string>,
         map(name: string): Any
     }
-    export function Mapped<T>(defaultKey: string, names: ([string, string] | [string, string, string])[], map: (name: string) => Any, info?: Info): Mapped<NamedParams<T>> {
+    export function Mapped<T>(defaultKey: string, names: ([string, string] | [string, string, string])[], map: (name: string) => Any, info?: Info & { cycle?: boolean }): Mapped<NamedParams<T>> {
         const name = checkDefaultKey(defaultKey, names);
         return setInfo<Mapped<NamedParams<T>>>({
             type: 'mapped',
@@ -218,7 +219,7 @@ export namespace ParamDefinition {
             map
         }, info);
     }
-    export function MappedStatic<C extends Params>(defaultKey: keyof C, map: C, info?: Info & { options?: [keyof C, string][] }): Mapped<NamedParamUnion<C>> {
+    export function MappedStatic<C extends Params>(defaultKey: keyof C, map: C, info?: Info & { options?: [keyof C, string][], cycle?: boolean }): Mapped<NamedParamUnion<C>> {
         const options: [string, string][] = info && info.options
             ? info.options as [string, string][]
             : Object.keys(map).map(k => [k, map[k].label || stringToWords(k)]) as [string, string][];