import * as React  from 'react';

interface IClickOutsideProps extends React.HTMLProps<HTMLDivElement> {
    active: boolean,
    children: any,
    onTrigger: (event: Event, isClick: boolean) => void,
    triggerOnClick: boolean,
    triggerOnEsc: boolean,
}

export class ClickOutside extends React.PureComponent<IClickOutsideProps> {
    public static defaultProps = {
        active: true,
        triggerOnClick: true,
        triggerOnEsc: true,
    };

    private wrapperRef = React.createRef<HTMLDivElement>();

    public componentDidMount() {
        const { active } = this.props;
        if (active) {
            this.bindEvents();
        }
    }

    public componentWillUnmount() {
        const { active } = this.props;
        if (active) {
            this.bindEvents();
        }
    }

    public componentDidUpdate(prevProps: IClickOutsideProps) {
        const { active } = this.props;
        if (active !== prevProps.active) {
            if (active) {
                this.bindEvents();
            } else {
                this.unbindEvents();
            }
        }
    }

    public bindEvents() {
        document.addEventListener('click', this.handleClick);
        document.addEventListener('keydown', this.handleKeyDown);
    }

    public unbindEvents = () => {
        document.removeEventListener('click', this.handleClick);
        document.removeEventListener('keydown', this.handleKeyDown);
    };

    public handleClick = (event: Event) => {
        const { triggerOnClick, onTrigger } = this.props;
        const { target, type } = event;
        const isClick = type === 'click';

        if (triggerOnClick && !this.wrapperRef.current!.contains(target as Element)) {
            onTrigger(event, isClick);
        }
    };

    public handleKeyDown = (event: KeyboardEvent) => {
        const { triggerOnEsc, onTrigger } = this.props;

        if (triggerOnEsc && event.key === 'Escape') {
            onTrigger(event, false);
        }
    };

    public render() {
        const { active, children, onTrigger, triggerOnClick, triggerOnEsc, ...rest } = this.props;

        return (
            <div {...rest} ref={this.wrapperRef}>
                {children}
            </div>
        );
    }
}
