import React, { useCallback, useEffect, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import Label, { LabelProps } from '../Label/Label'; import './Select.scss'; import classNames from 'classnames'; import Icon from '../Icon'; import useClickOutside from '../../hooks/useClickOutside'; export interface Option { label: string; value: string; disabled?: boolean; } export interface SelectProps { /** * Label properties */ labelProps?: LabelProps; /** * Label properties */ variant?: 'default' | 'approved' | 'review' | 'rejected'; /** * If true, Field label will be displayed else it will be hidden */ showLabel?: boolean; /** * Error | If true, error styles are applied */ error?: boolean; /** * Disabled | If true, disabled styles are applied and field becomes non interactive */ disabled?: boolean; /** * showBorder | If true, border will be added to select-box */ showBorder?: boolean; /** * showBorderOnHover | If true, border will be added to select-box on hover */ showBorderOnHover?: boolean; /** * SelectBox classes */ className?: string; /** * If error is true, this message will be displayed */ errorMsg?: string; /** * Options for the dropdown */ options: Option[] | []; /** * Seleceted Option */ selectedOption?: Option; /** * Placeholder | when the field is empty this will be displayed inside select box */ placeholder: string; /** * on change handler */ onChange?: (option: Option) => void; optionZindex?: string | number; } type DrowdownPosition = { posX: number; posY: number; width: number; fromBottom: number; }; export interface DropDownListProps { dropdownPosition: DrowdownPosition; options: Option[] | []; selectedOption?: Option; onOptionClickHandler: (option: Option) => void; onBlurHandler: () => void; optionZindex: string | number; } const DropDownList = ({ dropdownPosition, options, onOptionClickHandler, selectedOption, onBlurHandler, optionZindex, }: DropDownListProps) => { const optionsContainerRef = useRef(null); useClickOutside(optionsContainerRef, onBlurHandler); const selectDivHeight = 36; const marginHeight = 5; const singleOptionHeight = 35; const dropdownContainerPadding = 16; let dropdownContainerHeight = options.length > 5 ? 207 : options.length * singleOptionHeight + dropdownContainerPadding * 2; if (options.length === 0) { dropdownContainerHeight = 67; } useEffect(() => { if (optionsContainerRef.current !== document.activeElement) { optionsContainerRef?.current?.focus(); } }, [optionsContainerRef.current]); return ( <>
dropdownContainerHeight + marginHeight ? { left: dropdownPosition.posX, top: dropdownPosition.posY, width: dropdownPosition.width, zIndex: optionZindex, } : { left: dropdownPosition.posX, top: dropdownPosition.posY - selectDivHeight - dropdownContainerHeight - marginHeight, width: dropdownPosition.width, zIndex: optionZindex, marginTop: 0, } } className="storybook-select-options-container" >
{options.length ? ( options.map((option: Option) => { const isSelected = option.value === selectedOption?.value; return (

{ if (!option.disabled) { onOptionClickHandler(option); } }} > {option.label}

); }) ) : (

No Options

)}
); }; const Select = ({ options = [], selectedOption, placeholder = '', labelProps, disabled, className, errorMsg, error, onChange, showLabel = true, showBorder = true, showBorderOnHover = true, optionZindex = 999, variant = 'default', ...props }: SelectProps) => { const [showOptions, setShowOptions] = useState(false); const [dropdownPosition, setDropdownPosition] = useState({ posX: 0, posY: 0, width: 0, fromBottom: 0, }); const dropdownListRef = useRef(null); const onSelectDivClickHandler = () => { if (!showOptions && !disabled) { if (dropdownListRef?.current) { const rect = dropdownListRef.current.getBoundingClientRect(); setDropdownPosition({ posX: rect.left + window.scrollX, posY: rect.top + window.scrollY, width: dropdownListRef.current.offsetWidth, fromBottom: window.innerHeight - rect.top, }); } setShowOptions((prev) => !prev); } }; const onBlurHandler = useCallback(() => { setDropdownPosition({ posX: 0, posY: 0, width: 0, fromBottom: 0, }); setShowOptions(false); }, []); const onOptionClickHandler = (option: Option) => { onBlurHandler(); if (onChange) { onChange(option); } }; useEffect(() => { const updateElementPosition = () => { if (dropdownListRef.current) { const rect = dropdownListRef.current.getBoundingClientRect(); setDropdownPosition({ posX: rect.left + window.scrollX, posY: rect.top + window.scrollY, width: dropdownListRef.current.offsetWidth, fromBottom: window.innerHeight - rect.top, }); } }; const disableScroll = () => { const bodyScrollWidth = window.innerWidth - document.body.clientWidth; if (document.body.scrollHeight > window.innerHeight) { document.body.style.paddingRight = `${bodyScrollWidth}px`; } document.body.style.overflow = 'hidden'; }; const enableScroll = () => { document.body.style.paddingRight = ''; document.body.style.overflow = ''; }; if (showOptions) { disableScroll(); } else { enableScroll(); } const handleResize = () => { updateElementPosition(); }; window.addEventListener('resize', handleResize); window.addEventListener('scroll', handleResize); return () => { enableScroll(); window.removeEventListener('resize', handleResize); window.removeEventListener('scroll', handleResize); }; }, [showOptions]); return (
{showLabel && (
); }; export default Select;