LyDropDown.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. "use client";
  2. import { useState, useRef, useLayoutEffect, type ReactNode, useEffect } from "react";
  3. import clsx from "clsx";
  4. export function LyDropDown({
  5. showDropDown,
  6. children,
  7. onClose
  8. }: {
  9. showDropDown: boolean;
  10. children: ReactNode;
  11. onClose: () => void;
  12. }) {
  13. const listRef = useRef<HTMLDivElement>(null);
  14. const rootRef = useRef<HTMLDivElement>(null);
  15. const [listPosition, setListPosition] = useState({
  16. top: '0',
  17. transform: 'translate3d(0, 0%, 0)',
  18. });
  19. useLayoutEffect(() => {
  20. if (listRef.current && rootRef.current && showDropDown) {
  21. const { height } = listRef.current.getBoundingClientRect();
  22. // setListHeight(height);
  23. console.log('Measured tooltip height: ' + height);
  24. const triggerRect = rootRef.current.getBoundingClientRect();
  25. if(triggerRect.bottom + height > window.innerHeight) { // 显示在上方
  26. setListPosition({
  27. top: '0',
  28. transform: 'translate3d(0, -100%, 0)',
  29. });
  30. } else { // 显示在下方
  31. setListPosition({
  32. top: `${triggerRect.height}px`,
  33. transform: `translate3d(0, 0%, 0)`
  34. });
  35. }
  36. }
  37. }, [showDropDown]);
  38. // 点击外部关闭
  39. useEffect(() => {
  40. if (!showDropDown) return;
  41. function handleClickOutside(event: MouseEvent) {
  42. const target = event.target as Node;
  43. if (
  44. rootRef.current &&
  45. !rootRef.current.contains(target)
  46. ) {
  47. onClose();
  48. }
  49. }
  50. // 放到 document 捕获阶段
  51. document.addEventListener(
  52. "pointerdown",
  53. handleClickOutside,
  54. true
  55. );
  56. return () => {
  57. document.removeEventListener(
  58. "pointerdown",
  59. handleClickOutside,
  60. true
  61. );
  62. };
  63. }, [showDropDown, onClose]);
  64. return (
  65. <>
  66. <div className="w-full relative" ref={rootRef}>
  67. <div ref={listRef} className={clsx(
  68. "absolute w-full max-h-48 overflow-auto bg-white ly-thin-shadow border border-ly-gray rounded-md z-1",
  69. showDropDown ? "visible pointer-events-auto" : "invisible pointer-events-none"
  70. )}
  71. onClick={(e) => { e.stopPropagation(); }}
  72. style={listPosition}
  73. >
  74. {children}
  75. </div>
  76. </div>
  77. </>
  78. );
  79. }