-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Kenji Shiroma
committed
Jan 12, 2025
1 parent
345c765
commit be65c6b
Showing
11 changed files
with
248 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 113 additions & 0 deletions
113
packages/ui/src/components/popover/components/panel/panel.hook.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import { useMemo } from 'react'; | ||
|
||
import { PanelProps } from './panel.types.js'; | ||
|
||
const PANEL_WIDTH_SIZE = 282; | ||
|
||
const getVerticalPositionPopover = (element: HTMLDivElement) => { | ||
const triggerDOMRect = element.getBoundingClientRect(); | ||
|
||
const offsetLeftToCenter = (PANEL_WIDTH_SIZE - (triggerDOMRect?.width || 0) / 2) * -1; | ||
if (triggerDOMRect.left + offsetLeftToCenter <= 0) { | ||
return 'left'; | ||
} | ||
if ( | ||
triggerDOMRect.left + offsetLeftToCenter >= 0 && | ||
triggerDOMRect.right + offsetLeftToCenter * -1 <= window.innerWidth | ||
) { | ||
return 'center'; | ||
} | ||
if (PANEL_WIDTH_SIZE + (triggerDOMRect?.left || 0) >= window.innerWidth) { | ||
return 'right'; | ||
} | ||
}; | ||
|
||
const getLeftOffsetPerVerticalPosition = (element: HTMLDivElement) => { | ||
const triggerDOMRect = element.getBoundingClientRect(); | ||
switch (getVerticalPositionPopover(element)) { | ||
case 'center': | ||
return ((PANEL_WIDTH_SIZE - (triggerDOMRect?.width || 0)) / 2) * -1; | ||
case 'right': | ||
return ( | ||
(PANEL_WIDTH_SIZE + | ||
(triggerDOMRect?.left || 0) - | ||
window.innerWidth + | ||
(window.innerWidth - (triggerDOMRect?.right || 0))) * | ||
-1 | ||
); | ||
default: | ||
return 0; | ||
} | ||
}; | ||
|
||
export type PanelHookProps = { | ||
placement: PanelProps['placement']; | ||
portal: PanelProps['portal']; | ||
state: PanelProps['state']; | ||
triggerRef: PanelProps['triggerRef']; | ||
}; | ||
|
||
/** | ||
* Custom hook to calculate the position of a popover panel relative to its trigger element. | ||
* @returns {Object} An object containing the calculated positions for the popover and its arrow. | ||
* @returns {Object} return.popoverPosition - The calculated position styles for the popover. | ||
* @returns {Object} return.arrowPosition - The calculated position styles for the popover arrow. | ||
*/ | ||
export function usePanel({ state, placement = 'bottom', triggerRef, portal }: PanelHookProps) { | ||
const popoverPosition = useMemo(() => { | ||
const triggerDOMRect = triggerRef.current?.getBoundingClientRect(); | ||
// The offset is calculated according if the popover will overflow the window | ||
const leftOffset = triggerRef.current ? getLeftOffsetPerVerticalPosition(triggerRef.current) : 0; | ||
// If it is not portal, we can simplify the logic | ||
if (!portal) { | ||
switch (placement) { | ||
case 'top': | ||
return { | ||
bottom: '100%', | ||
left: leftOffset, | ||
}; | ||
case 'bottom': | ||
default: | ||
return { | ||
top: '100%', | ||
left: leftOffset, | ||
}; | ||
} | ||
} | ||
|
||
// If it is portal, we need to considerate the scroll if there is a scroll in the portal | ||
const portalElement = portal as Element; | ||
switch (placement) { | ||
case 'top': | ||
return { | ||
// The top is calculated according to the portal element | ||
top: `${(triggerDOMRect?.top || 0) - portalElement.getBoundingClientRect().top}px`, | ||
left: `${(triggerDOMRect?.left || 0) + leftOffset}px`, | ||
transform: 'translateY(-100%)', | ||
}; | ||
case 'bottom': | ||
default: | ||
return { | ||
// The top is calculated according to the portal element | ||
top: `${(triggerDOMRect?.bottom || 0) - portalElement.getBoundingClientRect().top}px`, | ||
left: `${(triggerDOMRect?.left || 0) + leftOffset}px`, | ||
}; | ||
} | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [placement, portal, triggerRef, state.isOpen]); | ||
|
||
const arrowPosition = useMemo(() => { | ||
const triggerDOMRect = triggerRef.current?.getBoundingClientRect(); | ||
const leftOffset = triggerRef.current ? getLeftOffsetPerVerticalPosition(triggerRef.current) * -1 : 0; | ||
|
||
return { | ||
left: `${(triggerDOMRect?.width || 0) / 2 + leftOffset}px`, | ||
}; | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [triggerRef, state.isOpen]); | ||
|
||
return { | ||
popoverPosition, | ||
arrowPosition, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.