UNPKG

@dr.pogodin/react-utils

Version:

Collection of generic ReactJS components and utils

8 lines 3.11 kB
import{useEffect,useRef,useState}from"react";import themed from"@dr.pogodin/react-themes";import{optionValueName}from"../common.js";import Options,{areEqual}from"./Options/index.js";const defaultTheme={"context":"SNj3wp","ad":"T6eOJ-","hoc":"KG-OKN","container":"SzHHPE","label":"KTG4ai","dropdown":"xdotm7","option":"y-WCDM","select":"PP5Siy","arrow":"_7-A7Lh","active":"RoOAZK","upward":"_--5Xpy"};import{Fragment as _Fragment,jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";const BaseCustomDropdown=({filter,label,onChange,options,theme,value})=>{const[active,setActive]=useState(false);const dropdownRef=useRef(null);const opsRef=useRef(null);const[opsPos,setOpsPos]=useState();const[upward,setUpward]=useState(false);useEffect(()=>{if(!active)return undefined;let id;const cb=()=>{const anchor=dropdownRef.current?.getBoundingClientRect();const opsRect=opsRef.current?.measure();if(anchor&&opsRect){const fitsDown=anchor.bottom+opsRect.height<(window.visualViewport?.height??0);const fitsUp=anchor.top-opsRect.height>0;const up=!fitsDown&&fitsUp;setUpward(up);const pos=up?{left:anchor.left,top:anchor.top-opsRect.height-1,width:anchor.width}:{left:anchor.left,top:anchor.bottom,width:anchor.width};setOpsPos(now=>areEqual(now,pos)?now:pos)}id=requestAnimationFrame(cb)};requestAnimationFrame(cb);return()=>{cancelAnimationFrame(id)}},[active]);const openList=e=>{const view=window.visualViewport;const rect=dropdownRef.current.getBoundingClientRect();setActive(true);// NOTE: This first opens the dropdown off-screen, where it is measured // by an effect declared above, and then positioned below, or above // the original dropdown element, depending where it fits best // (if we first open it downward, it would flick if we immediately // move it above, at least with the current position update via local // react state, and not imperatively). setOpsPos({left:view?.width??0,top:view?.height??0,width:rect.width});e.stopPropagation()};let selected=/*#__PURE__*/_jsx(_Fragment,{children:"\u200C"});for(const option of options){if(!filter||filter(option)){const[iValue,iName]=optionValueName(option);if(iValue===value){selected=iName;break}}}let containerClassName=theme.container;if(active)containerClassName+=` ${theme.active}`;let opsContainerClass=theme.select??"";if(upward){containerClassName+=` ${theme.upward}`;opsContainerClass+=` ${theme.upward}`}return/*#__PURE__*/_jsxs("div",{className:containerClassName,children:[label===undefined?null:/*#__PURE__*/_jsx("div",{className:theme.label,children:label}),/*#__PURE__*/_jsxs("div",{className:theme.dropdown,onClick:openList,onKeyDown:e=>{if(e.key==="Enter")openList(e)},ref:dropdownRef,role:"listbox",tabIndex:0,children:[selected,/*#__PURE__*/_jsx("div",{className:theme.arrow})]}),active?/*#__PURE__*/_jsx(Options,{containerClass:opsContainerClass,containerStyle:opsPos,onCancel:()=>{setActive(false)},onChange:newValue=>{setActive(false);if(onChange)onChange(newValue)},optionClass:theme.option??"",options:options,ref:opsRef}):null]})};export default themed(BaseCustomDropdown,"CustomDropdown",defaultTheme); //# sourceMappingURL=index.js.map