import classNames from 'classnames'; import React, { createContext, FC, FunctionComponentElement, useEffect, useRef, useState, } from 'react'; import useClickOutside from '../../hooks/useClickOutside'; import Input from '../Input'; import Transition from '../Transition/transition'; import { SelectOptionProps } from './option'; type Theme = 'dark' | 'light'; export interface SelectProps { defaultValue?: string | string[]; placeholder?: string; disabled?: boolean; name?: string; theme?: Theme; onChange?: (selectedValue: string, selectedValues: string[]) => void; onVisibleChange?: (visible: boolean) => void; } export interface ISelectContext { onSelect?: (value: string, isSelected?: boolean) => void; selectedValues: string[]; } export const SelectContext = createContext({ selectedValues: [], }); const findLabelFromValue = (value: string, options: Array) => { const option = options.find((item) => item.value === value); if (option) { return option.label; } return ''; }; export const Select: FC = (props) => { const { defaultValue, placeholder, children, name, theme, disabled, onChange, onVisibleChange, } = props; const input = useRef(null); const containerRef = useRef(null); const containerWidth = useRef(0); const [selectedValues] = useState( Array.isArray(defaultValue) ? defaultValue : [] ); const [menuOpen, setOpen] = useState(false); const [options, setOptions] = useState>([]); const [value, setValue] = useState( typeof defaultValue === 'string' ? findLabelFromValue(defaultValue, options) : '' ); const cInputNames = classNames('paypay-input-inner', { [`input-${theme}`]: theme && theme.length > 0, }); const handleOptionClick = (value: string, isSelected?: boolean) => { // update value setOpen(false); setValue(findLabelFromValue(value, options)); if (onVisibleChange) { onVisibleChange(false); } const updatedValues = [value]; if (onChange) { onChange(value, updatedValues); } }; useEffect(() => { // focus input if (input.current) { input.current.focus(); if (placeholder) { input.current.placeholder = placeholder; } } }, [selectedValues, placeholder]); useEffect(() => { if (containerRef.current) { containerWidth.current = containerRef.current.getBoundingClientRect().width; } }); useEffect(() => { const options = React.Children.map(children, (child) => { const childElement = child as FunctionComponentElement; return childElement.props; }); if (options) { setOptions(options); if (defaultValue && !Array.isArray(defaultValue)) { setValue(findLabelFromValue(defaultValue, options)); } } }, [children, defaultValue]); useClickOutside(containerRef, () => { setOpen(false); if (onVisibleChange && menuOpen) { onVisibleChange(false); } }); const passedContext: ISelectContext = { onSelect: handleOptionClick, selectedValues: selectedValues, }; const handleClick = (e: React.MouseEvent) => { e.preventDefault(); if (!disabled) { setOpen(!menuOpen); if (onVisibleChange) { onVisibleChange(!menuOpen); } } }; const generateOptions = () => { return React.Children.map(children, (child, i) => { const childElement = child as FunctionComponentElement; if (childElement.type.displayName === 'Option') { return React.cloneElement(childElement, { index: `select-${i}`, }); } else { console.error( 'Warning: Select has a child which is not a Option component' ); } }); }; const containerClass = classNames('paypay-select', { 'menu-is-open': menuOpen, 'is-disabled': disabled, }); return (
    {generateOptions()}
); }; Select.defaultProps = { name: 'paypay-select', placeholder: 'Please choose', theme: 'dark', }; export default Select;