0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

feat: add vertical center support (#2032)

Co-authored-by: wangsijie <wangsijie@silverhand.io>
This commit is contained in:
FlurryNight 2022-10-10 16:53:34 +01:00 committed by GitHub
parent 4b0970b6d8
commit 5eeb06e301
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 62 additions and 9 deletions

View file

@ -27,6 +27,16 @@
top: 0%;
}
&.arrowRight::after {
top: 50%;
left: 100%;
}
&.arrowLeft::after {
top: 50%;
left: 0%;
}
&.start::after {
left: _.unit(10);
}

View file

@ -2,7 +2,7 @@ import classNames from 'classnames';
import { ReactNode, RefObject, useEffect, useLayoutEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import usePosition, { HorizontalAlignment } from '@/hooks/use-position';
import usePosition, { VerticalAlignment, HorizontalAlignment } from '@/hooks/use-position';
import * as styles from './index.module.scss';
@ -11,15 +11,17 @@ type Props = {
anchorRef: RefObject<Element>;
className?: string;
isKeepOpen?: boolean;
verticalAlign?: VerticalAlignment;
horizontalAlign?: HorizontalAlignment;
flip?: 'right' | 'left';
};
const getHorizontalOffset = (alignment: HorizontalAlignment): number => {
const getHorizontalOffset = (alignment: HorizontalAlignment, flipped: string): number => {
switch (alignment) {
case 'start':
return -32;
return flipped === 'right' ? 32 : -32;
case 'end':
return 32;
return flipped === 'left' ? -32 : 32;
default:
return 0;
}
@ -30,15 +32,17 @@ const Tooltip = ({
anchorRef,
className,
isKeepOpen = false,
verticalAlign = 'top',
horizontalAlign = 'start',
flip,
}: Props) => {
const [tooltipDom, setTooltipDom] = useState<HTMLDivElement>();
const tooltipRef = useRef<HTMLDivElement>(null);
const { position, positionState, mutate } = usePosition({
verticalAlign: 'top',
verticalAlign,
horizontalAlign,
offset: { vertical: 16, horizontal: getHorizontalOffset(horizontalAlign) },
offset: { vertical: 16, horizontal: getHorizontalOffset(horizontalAlign, flip ?? '') },
anchorRef,
overlayRef: tooltipRef,
});
@ -119,6 +123,8 @@ const Tooltip = ({
}
const isArrowUp = positionState.verticalAlign === 'bottom';
const isArrowRight = flip === 'left' && positionState.horizontalAlign === 'end';
const isArrowLeft = flip === 'right' && positionState.horizontalAlign === 'start';
return createPortal(
<div
@ -126,7 +132,9 @@ const Tooltip = ({
className={classNames(
styles.tooltip,
isArrowUp && styles.arrowUp,
styles[horizontalAlign],
isArrowRight && styles.arrowRight,
isArrowLeft && styles.arrowLeft,
!flip && styles[horizontalAlign],
className
)}
style={{ ...position }}

View file

@ -1,6 +1,6 @@
import { RefObject, useCallback, useEffect, useState } from 'react';
export type VerticalAlignment = 'top' | 'bottom';
export type VerticalAlignment = 'top' | 'center' | 'bottom';
export type HorizontalAlignment = 'start' | 'center' | 'end';
@ -28,11 +28,13 @@ const windowSafePadding = 12;
const selectVerticalAlignment = ({
verticalAlign,
verticalTop,
verticalCenter,
verticalBottom,
overlayHeight,
}: {
verticalAlign: VerticalAlignment;
verticalTop: number;
verticalCenter: number;
verticalBottom: number;
overlayHeight: number;
}) => {
@ -40,6 +42,8 @@ const selectVerticalAlignment = ({
const maxY = window.innerHeight - windowSafePadding;
const isTopAllowed = verticalTop >= minY;
const isCenterAllowed =
verticalCenter - overlayHeight / 2 >= minY && verticalCenter + overlayHeight / 2 <= maxY;
const isBottomAllowed = verticalBottom + overlayHeight <= maxY;
switch (verticalAlign) {
@ -52,6 +56,26 @@ const selectVerticalAlignment = ({
return 'bottom';
}
if (isCenterAllowed) {
return 'center';
}
return verticalAlign;
}
case 'center': {
if (isCenterAllowed) {
return 'center';
}
if (isTopAllowed) {
return 'top';
}
if (isBottomAllowed) {
return 'bottom';
}
return verticalAlign;
}
@ -64,6 +88,10 @@ const selectVerticalAlignment = ({
return 'top';
}
if (isCenterAllowed) {
return 'center';
}
return verticalAlign;
}
@ -170,9 +198,15 @@ export default function usePosition({
const { scrollTop, scrollLeft } = document.documentElement;
const verticalTop = anchorRect.y - overlayRect.height + scrollTop - offset.vertical;
const verticalCenter =
anchorRect.y - anchorRect.height / 2 - overlayRect.height / 2 + scrollTop + offset.vertical;
const verticalBottom = anchorRect.y + anchorRect.height + scrollTop + offset.vertical;
const verticalPositionMap = { top: verticalTop, bottom: verticalBottom };
const verticalPositionMap = {
top: verticalTop,
center: verticalCenter,
bottom: verticalBottom,
};
const horizontalStart = anchorRect.x + scrollLeft + offset.horizontal;
const horizontalCenter =
@ -189,6 +223,7 @@ export default function usePosition({
const selectedVerticalAlign = selectVerticalAlignment({
verticalAlign,
verticalTop,
verticalCenter,
verticalBottom,
overlayHeight: overlayRect.height,
});