// @flow import { Select, SelectOption, useSelectState, useSelectPopover, SelectPopover, } from "@renderlesskit/react"; import { CheckmarkIcon } from "outline-icons"; import * as React from "react"; import { VisuallyHidden } from "reakit/VisuallyHidden"; import styled, { css } from "styled-components"; import Button, { Inner } from "components/Button"; import { Position, Background, Backdrop } from "./ContextMenu"; import { MenuAnchorCSS } from "./ContextMenu/MenuItem"; import { LabelText } from "./Input"; import useMenuHeight from "hooks/useMenuHeight"; export type Option = { label: string, value: string }; export type Props = { value?: string, label?: string, nude?: boolean, ariaLabel: string, short?: boolean, disabled?: boolean, className?: string, labelHidden?: boolean, options: Option[], onChange: (string) => Promise | void, }; const getOptionFromValue = (options: Option[], value) => { return options.find((option) => option.value === value) || {}; }; const InputSelect = (props: Props) => { const { value, label, className, labelHidden, options, short, ariaLabel, onChange, disabled, nude, } = props; const select = useSelectState({ gutter: 0, modal: true, selectedValue: value, }); const popOver = useSelectPopover({ ...select, hideOnClickOutside: true, preventBodyScroll: true, disabled, }); const previousValue = React.useRef(value); const buttonRef = React.useRef(); const minWidth = buttonRef.current?.offsetWidth || 0; const maxHeight = useMenuHeight( select.visible, select.unstable_disclosureRef ); React.useEffect(() => { if (previousValue.current === select.selectedValue) return; previousValue.current = select.selectedValue; async function load() { await onChange(select.selectedValue); } load(); }, [onChange, select.selectedValue]); const wrappedLabel = {label}; const selectedValueIndex = options.findIndex( (option) => option.value === select.selectedValue ); return ( <> {label && (labelHidden ? ( {wrappedLabel} ) : ( wrappedLabel ))} {(props) => { const topAnchor = props.style.top === "0"; const rightAnchor = props.placement === "bottom-end"; // offset top of select to place selected item under the cursor if (selectedValueIndex !== -1) { props.style.top = `-${(selectedValueIndex + 1) * 32}px`; } return ( {select.visible || select.animating ? options.map((option) => ( {select.selectedValue !== undefined && ( <> {select.selectedValue === option.value ? ( ) : ( )}   )} {option.label} )) : null} ); }} {(select.visible || select.animating) && } ); }; const Spacer = styled.svg` width: 24px; height: 24px; flex-shrink: 0; `; const StyledButton = styled(Button)` font-weight: normal; text-transform: none; margin-bottom: 16px; display: block; width: 100%; ${(props) => props.nude && css` border-color: transparent; box-shadow: none; `} ${Inner} { line-height: 28px; padding-left: 16px; padding-right: 8px; justify-content: space-between; } `; export const StyledSelectOption = styled(SelectOption)` ${MenuAnchorCSS} ${(props) => props.animating && css` pointer-events: none; `} `; const Wrapper = styled.label` display: block; max-width: ${(props) => (props.short ? "350px" : "100%")}; `; const Positioner = styled(Position)` &.focus-visible { ${StyledSelectOption} { &[aria-selected="true"] { color: ${(props) => props.theme.white}; background: ${(props) => props.theme.primary}; box-shadow: none; cursor: pointer; svg { fill: ${(props) => props.theme.white}; } } } } `; export default InputSelect;