github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ui/src/components/dropdown/dropdown.tsx (about)

     1  // Copyright 2020 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  import React from "react";
    12  import cn from "classnames";
    13  
    14  import { OutsideEventHandler } from "../outsideEventHandler";
    15  import "./dropdown.styl";
    16  import { Icon } from "antd";
    17  import { Button } from "src/components/button";
    18  
    19  export interface Item {
    20    value: string;
    21    name: React.ReactNode | string;
    22  }
    23  
    24  export interface DropdownProps {
    25    items: Array<Item>;
    26    onChange: (item: Item["value"]) => void;
    27    children: React.ReactNode;
    28    dropdownToggleButton?: () => React.ReactNode;
    29  }
    30  
    31  interface DropdownState {
    32    isOpen: boolean;
    33  }
    34  
    35  interface DropdownButtonProps {
    36    children: React.ReactNode;
    37    isOpen: boolean;
    38    onClick?: (event: React.MouseEvent<HTMLElement>) => void;
    39  }
    40  
    41  function DropdownButton(props: DropdownButtonProps) {
    42    const { children } = props;
    43    return (
    44      <Button
    45        type="flat"
    46        size="small"
    47        iconPosition="right"
    48        icon={() => (
    49          <Icon
    50            className="collapse-toggle__icon"
    51            type="caret-down" />
    52        )}
    53      >
    54        {children}
    55      </Button>
    56    );
    57  }
    58  
    59  export class Dropdown extends React.Component<DropdownProps, DropdownState> {
    60    state = {
    61      isOpen: false,
    62    };
    63  
    64    handleMenuOpen = () => {
    65      this.setState({
    66        isOpen: !this.state.isOpen,
    67      });
    68    }
    69  
    70    changeMenuState = (nextState: boolean) => {
    71      this.setState({
    72        isOpen: nextState,
    73      });
    74    }
    75  
    76    handleItemSelection = (value: string) => {
    77      this.props.onChange(value);
    78      this.handleMenuOpen();
    79    }
    80  
    81    renderDropdownToggleButton = () => {
    82      const { children, dropdownToggleButton } = this.props;
    83      const { isOpen } = this.state;
    84  
    85      if (dropdownToggleButton) {
    86        return dropdownToggleButton();
    87      } else {
    88        return (
    89          <DropdownButton
    90            isOpen={isOpen}
    91          >
    92            {children}
    93          </DropdownButton>
    94        );
    95      }
    96    }
    97  
    98    render() {
    99      const { items } = this.props;
   100      const { isOpen } = this.state;
   101  
   102      const menuStyles = cn(
   103        "crl-dropdown__menu",
   104        {
   105          "crl-dropdown__menu--open": isOpen,
   106        },
   107      );
   108  
   109      const menuItems = items.map((menuItem, idx) => (
   110        <DropdownItem
   111          value={menuItem.value}
   112          onClick={this.handleItemSelection}
   113          key={idx}
   114        >
   115          { menuItem.name }
   116        </DropdownItem>
   117      ));
   118  
   119      return (
   120        <div className="crl-dropdown">
   121          <OutsideEventHandler
   122            onOutsideClick={() => this.changeMenuState(false)}
   123          >
   124            <div
   125              className="crl-dropdown__handler"
   126              onClick={this.handleMenuOpen}>
   127              { this.renderDropdownToggleButton() }
   128            </div>
   129            <div className="crl-dropdown__overlay">
   130              <div className={menuStyles}>
   131                <div className="crl-dropdown__container">
   132                  {menuItems}
   133                </div>
   134              </div>
   135            </div>
   136          </OutsideEventHandler>
   137        </div>
   138      );
   139    }
   140  }
   141  
   142  export interface DropdownItemProps {
   143    children: React.ReactNode;
   144    value: string;
   145    onClick: (value: string) => void;
   146  }
   147  
   148  export function DropdownItem(props: DropdownItemProps) {
   149    const { children, value, onClick } = props;
   150    return (
   151      <div
   152        onClick={() => onClick(value)}
   153        className="crl-dropdown__item"
   154      >
   155        { children }
   156      </div>
   157    );
   158  }