Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | /* eslint-disable max-lines-per-function */ import React, { FocusEventHandler, ForwardedRef, RefObject, useMemo, useRef } from "react" import { prepareComponent, useToggle } from "@bluframe/grapple" import Select from "./Select" export type OptionValue = number | string export interface Option { label: React.ReactNode value: OptionValue } export interface Props { defaultValue?: string className?: string label: React.ReactNode name: string onBlur?: FocusEventHandler // eslint-disable-next-line no-unused-vars onChange: (value: OptionValue | null) => void options: Option[] placeholder?: string ref?: ForwardedRef<HTMLInputElement> value?: OptionValue | null } export interface ComponentProps extends Props { displayValue: React.ReactNode isControlled: boolean isLabelShrunk: boolean isOpen: boolean // eslint-disable-next-line no-unused-vars onChange: (value: OptionValue | null) => () => void onToggleIsOpen: () => void wrapperRef: RefObject<HTMLDivElement> } const DEFAULT_IS_OPEN = false const usePrepareComponent = (props: Props): ComponentProps => { const [isOpen, toggleIsOpen] = useToggle(DEFAULT_IS_OPEN) // Determine if the component is controlled const isControlled = props.value !== undefined const wrapperRef = useRef<HTMLDivElement>(null) // Get displayValue and isLabelShrunk const [displayValue, isLabelShrunk] = useMemo(() => { // Get selected option const selectedOption = props.options.find( (option) => (!isControlled && option.value === props.defaultValue) || option.value === props.value ) // If selected option exists, return its label and shrink the label Iif (selectedOption) { return [selectedOption.label, true] } // If no selected option and placeholder exists, return placeholder and shrink the label Iif (props.placeholder) { return [props.placeholder, true] } // Otherwise, return the label and shrink it when options are open return ["", isOpen] }, [ props.defaultValue, props.options, props.value, props.placeholder, isControlled, isOpen ]) const onBlur: FocusEventHandler = (event) => { Iif (props.onBlur) { props.onBlur(event) } } const onToggleIsOpen = () => { // If the dropdown is currently open and is being closed by a click, trigger onBlur Iif (isOpen) { Iif (wrapperRef.current) { wrapperRef.current.blur() } } // Toggle options dropdown toggleIsOpen() } const onChange = (value: OptionValue | null) => () => { // Select via onSelect from props props.onChange(value) // If open close options dropdown Iif (isOpen) { onToggleIsOpen() } } return { ...props, displayValue, isControlled, isLabelShrunk, isOpen, onBlur, onChange, onToggleIsOpen, wrapperRef } } export default prepareComponent<Props, ComponentProps>(usePrepareComponent)( Select ) |