diff --git a/web/components/dropdown.css b/web/components/dropdown.css new file mode 100644 --- /dev/null +++ b/web/components/dropdown.css @@ -0,0 +1,90 @@ +.dropdownContainer { + display: flex; + justify-content: center; + width: 100%; + padding: 20px; +} + +.dropdownMenu { + display: flex; + align-items: center; + width: 100%; + height: 45px; + max-width: 350px; + padding: 0 10px; + background-color: var(--modal-bg); + border-color: var(--dropdown-select-border); + border-radius: 5px; + border-style: solid; +} + +.dropdownMenu { + cursor: pointer; +} + +.dropdownDisplayText { + color: var(--change-member-role-modal-generic-text); + font-family: var(--font-stack); + padding: 5px; +} + +.dropdownIcon { + margin-left: auto; + font-size: var(--m-font-16); +} + +.dropdownList { + display: flex; + flex-direction: column; + align-items: center; + max-width: 370px; + max-height: 200px; + overflow-y: auto; + padding: 0 10px; + list-style: none; + margin-top: -15px; + width: 100%; +} + +.dropdownListItem { + color: var(--dropdown-text); + background-color: var(--dropdown-option-bg); + width: 100%; + height: 45px; + display: flex; + align-items: center; +} + +.dropdownListItem:hover { + background-color: var(--dropdown-option-hover-bg); + cursor: pointer; +} + +.dropdownListItemButton { + background-color: transparent; + background-repeat: no-repeat; + border: none; + cursor: pointer; + overflow: hidden; +} + +.dropdownListDisplayText { + color: var(--change-member-role-modal-generic-text); + font-family: var(--font-stack); + padding: 15px; +} + +.dropdownListCheckIcon { + margin-left: auto; + margin-right: 10px; + color: var(--dropdown-selected-option-check-color); +} + +.dropdownDisabled { + color: var(--dropdown-disabled-color); + border-color: var(--dropdown-disabled-color); +} + +.dropdownDisabled:hover { + cursor: not-allowed; +} diff --git a/web/components/dropdown.react.js b/web/components/dropdown.react.js new file mode 100644 --- /dev/null +++ b/web/components/dropdown.react.js @@ -0,0 +1,106 @@ +// @flow + +import classNames from 'classnames'; +import invariant from 'invariant'; +import * as React from 'react'; + +import SWMansionIcon from 'lib/components/SWMansionIcon.react.js'; + +import css from './dropdown.css'; + +type DropdownOption = { + +id: string, + +name: string, +}; + +type DropdownProps = { + +options: $ReadOnlyArray, + +activeSelection: string, + +setActiveSelection: string => mixed, + +disabled?: boolean, +}; + +function Dropdown(props: DropdownProps): React.Node { + const { options, activeSelection, setActiveSelection, disabled } = props; + const [isOpen, setIsOpen] = React.useState(false); + + const dropdownMenuClassNames = classNames({ + [css.dropdownMenu]: true, + [css.dropdownDisabled]: !!disabled, + }); + + const dropdownTextClassNames = classNames({ + [css.dropdownDisplayText]: true, + [css.dropdownDisabled]: !!disabled, + }); + + const toggleMenu = React.useCallback(() => { + if (disabled) { + return; + } + + setIsOpen(!isOpen); + }, [disabled, isOpen]); + + const handleSelection = React.useCallback( + selection => { + setActiveSelection(selection.id); + setIsOpen(false); + }, + [setActiveSelection], + ); + + const activeDisplayedOption = React.useMemo(() => { + const activeOption = options.find(option => option.id === activeSelection); + invariant(activeOption, 'Active option must be in options list'); + return activeOption.name; + }, [activeSelection, options]); + + const dropdownList = React.useMemo(() => { + if (!isOpen) { + return null; + } + + const dropdownOptions = options.map(option => { + const checkIcon = + option.id === activeSelection ? ( + + ) : null; + + return ( +
  • handleSelection(option)} + > + +
    {checkIcon}
    +
  • + ); + }); + + return ; + }, [activeSelection, handleSelection, isOpen, options]); + + return ( + <> +
    +
    +

    {activeDisplayedOption}

    +
    + +
    +
    +
    + {dropdownList} + + ); +} + +export default Dropdown; diff --git a/web/theme.css b/web/theme.css --- a/web/theme.css +++ b/web/theme.css @@ -226,4 +226,11 @@ --modal-overlay-background-90: rgba(0, 0, 0, 0.9); --modal-overlay-background-80: rgba(0, 0, 0, 0.8); --edit-avatar-button: var(--violet-dark-60); + --dropdown-text: var(--shades-white-100); + --dropdown-select-bg: var(--shades-black-90); + --dropdown-select-border: var(--shades-white-60); + --dropdown-option-bg: var(--shades-black-80); + --dropdown-option-hover-bg: var(--shades-black-70); + --dropdown-selected-option-check-color: var(--violet-dark-100); + --dropdown-disabled-color: var(--shades-black-60); }