From 5d738b4fe832ef1c554448440ede237c32632d10 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 12 Feb 2025 12:54:53 -0500 Subject: cleaning up how text boxes maintain focus when UI widgets are interacted with. --- packages/components/src/components/Popup/Popup.tsx | 281 ++++++++++----------- 1 file changed, 136 insertions(+), 145 deletions(-) (limited to 'packages/components/src') diff --git a/packages/components/src/components/Popup/Popup.tsx b/packages/components/src/components/Popup/Popup.tsx index 5a1179c69..92e7227bd 100644 --- a/packages/components/src/components/Popup/Popup.tsx +++ b/packages/components/src/components/Popup/Popup.tsx @@ -1,32 +1,32 @@ -import React, { useEffect, useRef, useState } from 'react' -import { Colors, IGlobalProps, Placement, Size , getFormLabelSize, isDark } from '../../global' -import { Toggle, ToggleType } from '../Toggle' -import './Popup.scss' -import { Popper } from '@mui/material' +import React, { useEffect, useRef, useState } from 'react'; +import { Colors, IGlobalProps, Placement, Size, getFormLabelSize, isDark } from '../../global'; +import { Toggle, ToggleType } from '../Toggle'; +import './Popup.scss'; +import { Popper } from '@mui/material'; export enum PopupTrigger { - CLICK = "click", - HOVER = "hover", - HOVER_DELAY = "hover_delay" + CLICK = 'click', + HOVER = 'hover', + HOVER_DELAY = 'hover_delay', } export interface IPopupProps extends IGlobalProps { - text?: string - icon?: JSX.Element | string, - iconPlacement?: Placement, - placement?: Placement, - size?: Size - height?: number - toggle?: JSX.Element; - popup: JSX.Element | string | (() => JSX.Element) - trigger?: PopupTrigger - toggleStatus?: boolean; - isOpen?: boolean; - setOpen?: (b: boolean) => void; - background?: string, - isToggle?: boolean; - toggleFunc?: () => void; - popupContainsPt?: (x:number, y:number) => boolean; + text?: string; + icon?: JSX.Element | string; + iconPlacement?: Placement; + placement?: Placement; + size?: Size; + height?: number; + toggle?: JSX.Element; + popup: JSX.Element | string | (() => JSX.Element); + trigger?: PopupTrigger; + toggleStatus?: boolean; + isOpen?: boolean; + setOpen?: (b: boolean) => void; + background?: string; + isToggle?: boolean; + toggleFunc?: () => void; + popupContainsPt?: (x: number, y: number) => boolean; } /** @@ -38,130 +38,121 @@ export interface IPopupProps extends IGlobalProps { * Look at: import Select from "react-select"; */ export const Popup = (props: IPopupProps) => { - - const [locIsOpen, locSetOpen] = useState(false) + const [locIsOpen, locSetOpen] = useState(false); - const { - text, - size, - icon, - popup, - type, - color, - isOpen = locIsOpen, - setOpen = locSetOpen, - toggle, - tooltip, - trigger = PopupTrigger.CLICK, - placement = 'bottom-start', - width, - height, - fillWidth, - iconPlacement = 'left', - background = isDark(color) ? Colors.LIGHT_GRAY : Colors.DARK_GRAY, - popupContainsPt - } = props - - const triggerRef = useRef(null); - const popperRef = useRef(null); + const { + text, + size, + icon, + popup, + type, + color, + isOpen = locIsOpen, + setOpen = locSetOpen, + toggle, + tooltip, + trigger = PopupTrigger.CLICK, + placement = 'bottom-start', + width, + height, + fillWidth, + iconPlacement = 'left', + background = isDark(color) ? Colors.LIGHT_GRAY : Colors.DARK_GRAY, + popupContainsPt, + } = props; - let timeout = setTimeout(() => {}); + const triggerRef = useRef(null); + const popperRef = useRef(null); - const handlePointerAwayDown = (e: PointerEvent) => { - const rect = (popperRef.current as any)?.getBoundingClientRect(); - if (rect && !(rect.left < e.clientX && rect.top < e.clientY && rect.right > e.clientX && rect.bottom > e.clientY) && - !popupContainsPt?.(e.clientX, e.clientY)) { - e.preventDefault(); - setOpen(false); - } - } + let timeout = setTimeout(() => {}); - useEffect(() => { - if (isOpen) { - window.removeEventListener("pointerdown", handlePointerAwayDown, {capture:true}) - window.addEventListener("pointerdown", handlePointerAwayDown, {capture:true}); - return () => { - window.removeEventListener("pointerdown", handlePointerAwayDown, {capture:true}); - } - }}, [isOpen, popupContainsPt]) - - return ( -
-
{ - if (trigger === PopupTrigger.CLICK) setOpen (!isOpen) - }} - onPointerEnter={() => { - if (trigger === PopupTrigger.HOVER || trigger === PopupTrigger.HOVER_DELAY) { - clearTimeout(timeout); - setOpen(true) - } - }} - onPointerLeave={() => { - if (trigger === PopupTrigger.HOVER || trigger === PopupTrigger.HOVER_DELAY) { - timeout = setTimeout(() => setOpen(false), 1000); - } - }} - > - {toggle - ? - toggle - : - { - if (trigger === PopupTrigger.CLICK) { - if (!props.isToggle || props.toggleStatus) { - setOpen(!isOpen) - } - props.toggleFunc?.(); - } - }} - fillWidth={fillWidth} - /> + const handlePointerAwayDown = (e: PointerEvent) => { + const rect = (popperRef.current as any)?.getBoundingClientRect(); + if (rect && !(rect.left < e.clientX && rect.top < e.clientY && rect.right > e.clientX && rect.bottom > e.clientY) && !popupContainsPt?.(e.clientX, e.clientY)) { + e.preventDefault(); + setOpen(false); } -
- -
{ - e.stopPropagation(); - }} - onPointerEnter={() => { - if (trigger === PopupTrigger.HOVER || trigger === PopupTrigger.HOVER_DELAY) { - clearTimeout(timeout); - setOpen(true); - } - }} - onPointerLeave={() => { - if (trigger === PopupTrigger.HOVER || trigger === PopupTrigger.HOVER_DELAY) { - timeout = setTimeout(() => setOpen(false), 200); - } - }} - > - {!isOpen ? (null): typeof popup === 'function' ? popup() : popup} -
-
-
- ) -} + }; + + useEffect(() => { + if (isOpen) { + window.removeEventListener('pointerdown', handlePointerAwayDown, { capture: true }); + window.addEventListener('pointerdown', handlePointerAwayDown, { capture: true }); + return () => { + window.removeEventListener('pointerdown', handlePointerAwayDown, { capture: true }); + }; + } + }, [isOpen, popupContainsPt]); + return ( +
+
{ + if (trigger === PopupTrigger.CLICK) setOpen(!isOpen); + }} + onPointerEnter={() => { + if (trigger === PopupTrigger.HOVER || trigger === PopupTrigger.HOVER_DELAY) { + clearTimeout(timeout); + setOpen(true); + } + }} + onPointerLeave={() => { + if (trigger === PopupTrigger.HOVER || trigger === PopupTrigger.HOVER_DELAY) { + timeout = setTimeout(() => setOpen(false), 1000); + } + }}> + {toggle ? ( + toggle + ) : ( + { + if (trigger === PopupTrigger.CLICK) { + if (!props.isToggle || props.toggleStatus) { + setOpen(!isOpen); + } + props.toggleFunc?.(); + } + }} + fillWidth={fillWidth} + /> + )} +
+ +
{ + e.stopPropagation(); + }} + onPointerEnter={() => { + if (trigger === PopupTrigger.HOVER || trigger === PopupTrigger.HOVER_DELAY) { + clearTimeout(timeout); + setOpen(true); + } + }} + onPointerLeave={() => { + if (trigger === PopupTrigger.HOVER || trigger === PopupTrigger.HOVER_DELAY) { + timeout = setTimeout(() => setOpen(false), 200); + } + }}> + {!isOpen ? null : typeof popup === 'function' ? popup() : popup} +
+
+
+ ); +}; -- cgit v1.2.3-70-g09d2