diff options
author | bobzel <zzzman@gmail.com> | 2025-02-12 12:54:53 -0500 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2025-02-12 12:54:53 -0500 |
commit | 5d738b4fe832ef1c554448440ede237c32632d10 (patch) | |
tree | e2e7a4eabe4472dca1bc4144f41491c36a0138cd /packages/components/src | |
parent | 3e531aa898492cb05e25081f422bb59adab72e8e (diff) |
cleaning up how text boxes maintain focus when UI widgets are interacted with.
Diffstat (limited to 'packages/components/src')
-rw-r--r-- | packages/components/src/components/Popup/Popup.tsx | 281 |
1 files changed, 136 insertions, 145 deletions
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<boolean>(false) + const [locIsOpen, locSetOpen] = useState<boolean>(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 ( - <div className={`popup-wrapper ${fillWidth && 'fillWidth'}`} > - <div - className={`trigger-container ${fillWidth && 'fillWidth'}`} - ref={triggerRef} - onClick={() => { - 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 - : - <Toggle - tooltip={tooltip} - size={size} - type={type} - color={color} - background={props.isToggle ? undefined : background} - toggleType={ToggleType.BUTTON} - icon={icon} - iconPlacement={iconPlacement} - text={text} - label={props.label} - toggleStatus={isOpen || props.toggleStatus} - onClick={() => { - 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); } - </div> - <Popper - open={isOpen} - style={{zIndex: 20000}} - anchorEl={triggerRef.current} - placement={placement} - modifiers={[ - ]} - > - <div className={`popup-container`} ref={popperRef} - style={{width, height, background}} - onPointerDown={(e) => { - 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} - </div> - </Popper> - </div> - ) -} + }; + + 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 ( + <div className={`popup-wrapper ${fillWidth && 'fillWidth'}`}> + <div + className={`trigger-container ${fillWidth && 'fillWidth'}`} + ref={triggerRef} + onClick={() => { + 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 + ) : ( + <Toggle + tooltip={tooltip} + size={size} + type={type} + color={color} + background={props.isToggle ? undefined : background} + toggleType={ToggleType.BUTTON} + icon={icon} + iconPlacement={iconPlacement} + text={text} + label={props.label} + toggleStatus={isOpen || props.toggleStatus} + onClick={() => { + if (trigger === PopupTrigger.CLICK) { + if (!props.isToggle || props.toggleStatus) { + setOpen(!isOpen); + } + props.toggleFunc?.(); + } + }} + fillWidth={fillWidth} + /> + )} + </div> + <Popper open={isOpen} style={{ zIndex: 20000 }} anchorEl={triggerRef.current} placement={placement} modifiers={[]}> + <div + className={`popup-container`} + ref={popperRef} + style={{ width, height, background }} + tabIndex={-1} + onPointerDown={e => { + 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} + </div> + </Popper> + </div> + ); +}; |