Skip to content
Snippets Groups Projects
Select Git revision
  • d5bd72a70c5067930da651712305f00580580dec
  • master default protected
  • devel
  • hruska-feature-clients-api
  • malostik-#5066-deduplicate-idea-ids
  • warden-postgresql-port
  • hruska-feature-#6799-filter-keys
  • hruska-feature-5066-duplicateIdeaID
  • warden-client-3.0-beta3
  • warden-server-3.0-beta3
  • warden-client-2.2-final
  • warden-server-2.2-final
  • warden-client-3.0-beta2
  • warden-server-3.0-beta2
  • warden-client-2.2
  • warden-server-2.2-patch3
  • warden-client-3.0-beta1
  • warden-server-3.0-beta1
  • warden-server-2.2-patch1
  • warden-client-3.0-beta0
  • warden-server-3.0-beta0
  • warden-server-2.2
  • warden-server-2.1-patch1
  • warden-client-2.1
  • warden-server-2.1
  • warden-server-2.1-beta6
  • warden-server-2.1-beta5
  • warden-server-2.1-beta4
28 results

chck_modules_version.sh

Blame
  • Forked from 713 / Warden / Warden - archive
    Source project has a limited visibility.
    common.tsx 14.82 KiB
    /**
     * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
     *
     * @author David Sehnal <david.sehnal@gmail.com>
     */
    
    import * as React from 'react';
    import { Color } from '../../mol-util/color';
    import { IconName, Icon } from './icons';
    
    export class ControlGroup extends React.Component<{
        header: string,
        initialExpanded?: boolean,
        hideExpander?: boolean,
        hideOffset?: boolean,
        topRightIcon?: IconName,
        headerLeftMargin?: string,
        onHeaderClick?: () => void,
        noTopMargin?: boolean,
        childrenClassName?: string
    }, { isExpanded: boolean }> {
        state = { isExpanded: !!this.props.initialExpanded }
    
        headerClicked = () => {
            if (this.props.onHeaderClick) {
                this.props.onHeaderClick();
            } else {
                this.setState({ isExpanded: !this.state.isExpanded });
            }
        }
    
        render() {
            let groupClassName = this.props.hideOffset ? 'msp-control-group-children' : 'msp-control-group-children msp-control-offset';
            if (this.props.childrenClassName) groupClassName += ' ' + this.props.childrenClassName;
    
            // TODO: customize header style (bg color, togle button etc)
            return <div className='msp-control-group-wrapper' style={{ position: 'relative', marginTop: this.props.noTopMargin ? 0 : void 0 }}>
                <div className='msp-control-group-header' style={{ marginLeft: this.props.headerLeftMargin }}>
                    <Button onClick={this.headerClicked}>
                        {!this.props.hideExpander && <Icon name={this.state.isExpanded ? 'collapse' : 'expand'} />}
                        {this.props.topRightIcon && <Icon name={this.props.topRightIcon} style={{ position: 'absolute', right: '2px', top: 0 }} />}
                        <b>{this.props.header}</b>
                    </Button>
                </div>
                {this.state.isExpanded && <div className={groupClassName} style={{ display: this.state.isExpanded ? 'block' : 'none' }}>
                    {this.props.children}
                </div>}
            </div>
        }
    }
    
    export interface TextInputProps<T> {
        className?: string,
        style?: React.CSSProperties,
        value: T,
        fromValue?(v: T): string,
        toValue?(s: string): T,
        // TODO: add error/help messages here?
        isValid?(s: string): boolean,
        onChange(value: T): void,
        onEnter?(): void,
        onBlur?(): void,
        delayMs?: number,
        blurOnEnter?: boolean,
        blurOnEscape?: boolean,
        isDisabled?: boolean,
        placeholder?: string
    }
    
    interface TextInputState {
        originalValue: string,
        value: string
    }
    
    function _id(x: any) { return x; }
    
    export class TextInput<T = string> extends React.PureComponent<TextInputProps<T>, TextInputState> {
        private input = React.createRef<HTMLInputElement>();
        private delayHandle: any = void 0;
        private pendingValue: T | undefined = void 0;
    
        state = { originalValue: '', value: '' }
    
        onBlur = () => {
            this.setState({ value: '' + this.state.originalValue });
            if (this.props.onBlur) this.props.onBlur();
        }
    
        get isPending() { return typeof this.delayHandle !== 'undefined'; }
    
        clearTimeout() {
            if (this.isPending) {
                clearTimeout(this.delayHandle);
                this.delayHandle = void 0;
            }
        }
    
        raiseOnChange = () => {
            this.props.onChange(this.pendingValue!);
            this.pendingValue = void 0;
        }
    
        triggerChanged(formatted: string, converted: T) {
            this.clearTimeout();
    
            if (formatted === this.state.originalValue) return;
    
            if (this.props.delayMs) {
                this.pendingValue = converted;
                this.delayHandle = setTimeout(this.raiseOnChange, this.props.delayMs);
            } else {
                this.props.onChange(converted);
            }
        }
    
        onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
            const value = e.target.value;
    
            if (this.props.isValid && !this.props.isValid(value)) {
                this.clearTimeout();
                this.setState({ value });
                return;
            }
    
            const converted = (this.props.toValue || _id)(value);
            const formatted = (this.props.fromValue || _id)(converted);
            this.setState({ value: formatted }, () => this.triggerChanged(formatted, converted));
        }
    
        onKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => {
            if (e.charCode === 27 || e.keyCode === 27 /* esc */) {
                if (this.props.blurOnEscape && this.input.current) {
                    this.input.current.blur();
                }
            }
        }
    
        onKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
            if (e.keyCode === 13 || e.charCode === 13 /* enter */) {
                if (this.isPending) {
                    this.clearTimeout();
                    this.raiseOnChange();
                }
                if (this.props.blurOnEnter && this.input.current) {
                    this.input.current.blur();
                }
                if (this.props.onEnter) this.props.onEnter();
            }
            e.stopPropagation();
        }
    
        static getDerivedStateFromProps(props: TextInputProps<any>, state: TextInputState) {
            const value = props.fromValue ? props.fromValue(props.value) : props.value;
            if (value === state.originalValue) return null;
            return { originalValue: value, value };
        }
    
        render() {
            return <input type='text'
                className={this.props.className}
                style={this.props.style}
                ref={this.input}
                onBlur={this.onBlur}
                value={this.state.value}
                placeholder={this.props.placeholder}
                onChange={this.onChange}
                onKeyPress={this.props.onEnter || this.props.blurOnEnter || this.props.blurOnEscape ? this.onKeyPress : void 0}
                onKeyDown={this.props.blurOnEscape ? this.onKeyUp : void 0}
                disabled={!!this.props.isDisabled}
            />;
        }
    }
    
    // TODO: replace this with parametrized TextInput
    export class NumericInput extends React.PureComponent<{
        value: number,
        onChange: (v: number) => void,
        onEnter?: () => void,
        onBlur?: () => void,
        blurOnEnter?: boolean,
        isDisabled?: boolean,
        placeholder?: string
    }, { value: string }> {
        state = { value: '0' };
        input = React.createRef<HTMLInputElement>();
    
        onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
            const value = +e.target.value;
            this.setState({ value: e.target.value }, () => {
                if (!Number.isNaN(value) && value !== this.props.value) {
                    this.props.onChange(value);
                }
            });
        }
    
        onKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
            if ((e.keyCode === 13 || e.charCode === 13)) {
                if (this.props.blurOnEnter && this.input.current) {
                    this.input.current.blur();
                }
                if (this.props.onEnter) this.props.onEnter();
            }
            e.stopPropagation();
        }
    
        onBlur = () => {
            this.setState({ value: '' + this.props.value });
            if (this.props.onBlur) this.props.onBlur();
        }
    
        static getDerivedStateFromProps(props: { value: number }, state: { value: string }) {
            const value = +state.value;
            if (Number.isNaN(value) || value === props.value) return null;
            return { value: '' + props.value };
        }
    
        render() {
            return <input type='text'
                ref={this.input}
                onBlur={this.onBlur}
                value={this.state.value}
                placeholder={this.props.placeholder}
                onChange={this.onChange}
                onKeyPress={this.props.onEnter || this.props.blurOnEnter ? this.onKeyPress : void 0}
                disabled={!!this.props.isDisabled}
            />
        }
    }
    
    export class ExpandableControlRow extends React.Component<{
        label: string,
        colorStripe?: Color,
        pivot: JSX.Element,
        controls: JSX.Element
    }, { isExpanded: boolean }> {
        state = { isExpanded: false };
    
        toggleExpanded = () => this.setState({ isExpanded: !this.state.isExpanded });
    
        render() {
            const { label, pivot, controls } = this.props;
            // TODO: fix the inline CSS
            return <>
                <ControlRow label={<>
                    {label}
                    <button className='msp-btn-link msp-btn-icon msp-control-group-expander' onClick={this.toggleExpanded} title={`${this.state.isExpanded ? 'Less' : 'More'} options`}
                        style={{ background: 'transparent', textAlign: 'left', padding: '0' }}>
                        <Icon name={this.state.isExpanded ? 'minus' : 'plus'} style={{ display: 'inline-block' }} />
                    </button>
                </>} control={pivot}>
                    {this.props.colorStripe && <div className='msp-expandable-group-color-stripe' style={{ backgroundColor: Color.toStyle(this.props.colorStripe) }} />}
                </ControlRow>
                {this.state.isExpanded && <div className='msp-control-offset'>
                    {controls}
                </div>}
            </>;
        }
    }
    
    export function SectionHeader(props: { icon?: IconName, title: string | JSX.Element, desc?: string }) {
        return <div className='msp-section-header'>
            {props.icon && <Icon name={props.icon} />}
            {props.title} <small>{props.desc}</small>
        </div>
    }
    
    export type ButtonProps = {
        style?: React.CSSProperties,
        className?: string,
        disabled?: boolean,
        title?: string,
        icon?: IconName,
        children?: React.ReactNode,
        onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void,
        onContextMenu?: (e: React.MouseEvent<HTMLButtonElement>) => void,
        onMouseEnter?: (e: React.MouseEvent<HTMLButtonElement>) => void,
        onMouseLeave?: (e: React.MouseEvent<HTMLButtonElement>) => void,
        inline?: boolean,
        'data-id'?: string,
        'data-color'?: Color,
        flex?: boolean | string | number,
        noOverflow?: boolean
    }
    
    export function Button(props: ButtonProps) {
        let className = 'msp-btn';
        if (!props.inline) className += ' msp-btn-block';
        if (props.noOverflow) className += ' msp-no-overflow';
        if (props.flex) className += ' msp-flex-item';
        if (props.className) className += ' ' + props.className;
    
        let style: React.CSSProperties | undefined = void 0;
        if (props.flex) {
            if (typeof props.flex === 'number') style = { flex: `0 0 ${props.flex}px`, padding: 0, maxWidth: `${props.flex}px` };
            else if (typeof props.flex === 'string') style = { flex: `0 0 ${props.flex}`, padding: 0, maxWidth: props.flex };
        }
        if (props.style) {
            if (style) Object.assign(style, props.style);
            else style = props.style;
        }
    
        return <button onClick={props.onClick} title={props.title} disabled={props.disabled} style={style} className={className} data-id={props['data-id']} data-color={props['data-color']}
            onContextMenu={props.onContextMenu} onMouseEnter={props.onMouseEnter} onMouseLeave={props.onMouseLeave}>
            {props.icon && <Icon name={props.icon} />}
            {props.children}
        </button>;
    }
    
    export function IconButton(props: {
        icon: IconName,
        small?: boolean,
        onClick: (e: React.MouseEvent<HTMLButtonElement>) => void,
        title?: string,
        toggleState?: boolean,
        disabled?: boolean,
        className?: string,
        style?: React.CSSProperties,
        'data-id'?: string,
        extraContent?: JSX.Element,
        flex?: boolean | string | number,
        transparent?: boolean
    }) {
        let className = `msp-btn msp-btn-icon${props.small ? '-small' : ''}${props.className ? ' ' + props.className : ''}`;
        if (typeof props.toggleState !== 'undefined') {
            className += ` msp-btn-link-toggle-${props.toggleState ? 'on' : 'off'}`
        }
        if (props.transparent) {
            className += ' msp-transparent-bg'
        }
        const iconStyle = props.small ? { fontSize: '80%' } : void 0;
    
        let style: React.CSSProperties | undefined = void 0;
        if (props.flex) {
            if (typeof props.flex === 'boolean') style = { flex: '0 0 32px', padding: 0 };
            else if (typeof props.flex === 'number') style = { flex: `0 0 ${props.flex}px`, padding: 0, maxWidth: `${props.flex}px` };
            else style = { flex: `0 0 ${props.flex}`, padding: 0, maxWidth: props.flex };
        }
        if (props.style) {
            if (style) Object.assign(style, props.style);
            else style = props.style;
        }
    
        return <button className={className} onClick={props.onClick} title={props.title} disabled={props.disabled} data-id={props['data-id']} style={style}>
            <Icon name={props.icon} style={iconStyle} />
            {props.extraContent}
        </button>;
    }
    
    export type ToggleButtonProps = {
        style?: React.CSSProperties,
        className?: string,
        disabled?: boolean,
        label?: string | JSX.Element,
        title?: string,
        icon?: IconName,
        isSelected?: boolean,
        toggle: () => void
    }
    
    export class ToggleButton extends React.PureComponent<ToggleButtonProps> {
        onClick = (e: React.MouseEvent<HTMLButtonElement>) => {
            e.currentTarget.blur();
            this.props.toggle();
        }
    
        render() {
            const props = this.props;
            const label = props.label;
            const className = props.isSelected ? `${props.className || ''} msp-control-current` : props.className;
            return <Button icon={this.props.icon} onClick={this.onClick} title={this.props.title}
                disabled={props.disabled} style={props.style} className={className}>
                {label && this.props.isSelected ? <b>{label}</b> : label}
            </Button>;
        }
    }
    
    export class ExpandGroup extends React.PureComponent<{ header: string, headerStyle?: React.CSSProperties, initiallyExpanded?: boolean, noOffset?: boolean, marginTop?: 0 | string, headerLeftMargin?: string }, { isExpanded: boolean }> {
        state = { isExpanded: !!this.props.initiallyExpanded };
    
        toggleExpanded = () => this.setState({ isExpanded: !this.state.isExpanded });
    
        render() {
            return <>
                <div className='msp-control-group-header' style={{ marginTop: this.props.marginTop !== void 0 ? this.props.marginTop : '1px', marginLeft: this.props.headerLeftMargin }}>
                    <button className='msp-btn msp-form-control msp-btn-block' onClick={this.toggleExpanded} style={this.props.headerStyle}>
                        <Icon name={this.state.isExpanded ? 'collapse' : 'expand'} />
                        {this.props.header}
                    </button>
                </div>
                {this.state.isExpanded &&
                    (this.props.noOffset
                        ? this.props.children
                        : <div className='msp-control-offset'>
                            {this.props.children}
                        </div>)}
            </>;
        }
    }
    
    export type ControlRowProps = {
        title?: string,
        label?: React.ReactNode,
        control?: React.ReactNode,
        className?: string,
        children?: React.ReactNode
    }
    
    export function ControlRow(props: ControlRowProps) {
        let className = 'msp-control-row';
        if (props.className) className += ' ' + props.className;
        return <div className={className}>
            <span className='msp-control-row-label' title={props.title}>{props.label}</span>
            <div className='msp-control-row-ctrl'>{props.control}</div>
            {props.children}
        </div>;
    }