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:
parent
4b0970b6d8
commit
5eeb06e301
3 changed files with 62 additions and 9 deletions
|
@ -27,6 +27,16 @@
|
||||||
top: 0%;
|
top: 0%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.arrowRight::after {
|
||||||
|
top: 50%;
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.arrowLeft::after {
|
||||||
|
top: 50%;
|
||||||
|
left: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
&.start::after {
|
&.start::after {
|
||||||
left: _.unit(10);
|
left: _.unit(10);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import classNames from 'classnames';
|
||||||
import { ReactNode, RefObject, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
import { ReactNode, RefObject, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
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';
|
import * as styles from './index.module.scss';
|
||||||
|
|
||||||
|
@ -11,15 +11,17 @@ type Props = {
|
||||||
anchorRef: RefObject<Element>;
|
anchorRef: RefObject<Element>;
|
||||||
className?: string;
|
className?: string;
|
||||||
isKeepOpen?: boolean;
|
isKeepOpen?: boolean;
|
||||||
|
verticalAlign?: VerticalAlignment;
|
||||||
horizontalAlign?: HorizontalAlignment;
|
horizontalAlign?: HorizontalAlignment;
|
||||||
|
flip?: 'right' | 'left';
|
||||||
};
|
};
|
||||||
|
|
||||||
const getHorizontalOffset = (alignment: HorizontalAlignment): number => {
|
const getHorizontalOffset = (alignment: HorizontalAlignment, flipped: string): number => {
|
||||||
switch (alignment) {
|
switch (alignment) {
|
||||||
case 'start':
|
case 'start':
|
||||||
return -32;
|
return flipped === 'right' ? 32 : -32;
|
||||||
case 'end':
|
case 'end':
|
||||||
return 32;
|
return flipped === 'left' ? -32 : 32;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -30,15 +32,17 @@ const Tooltip = ({
|
||||||
anchorRef,
|
anchorRef,
|
||||||
className,
|
className,
|
||||||
isKeepOpen = false,
|
isKeepOpen = false,
|
||||||
|
verticalAlign = 'top',
|
||||||
horizontalAlign = 'start',
|
horizontalAlign = 'start',
|
||||||
|
flip,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [tooltipDom, setTooltipDom] = useState<HTMLDivElement>();
|
const [tooltipDom, setTooltipDom] = useState<HTMLDivElement>();
|
||||||
const tooltipRef = useRef<HTMLDivElement>(null);
|
const tooltipRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const { position, positionState, mutate } = usePosition({
|
const { position, positionState, mutate } = usePosition({
|
||||||
verticalAlign: 'top',
|
verticalAlign,
|
||||||
horizontalAlign,
|
horizontalAlign,
|
||||||
offset: { vertical: 16, horizontal: getHorizontalOffset(horizontalAlign) },
|
offset: { vertical: 16, horizontal: getHorizontalOffset(horizontalAlign, flip ?? '') },
|
||||||
anchorRef,
|
anchorRef,
|
||||||
overlayRef: tooltipRef,
|
overlayRef: tooltipRef,
|
||||||
});
|
});
|
||||||
|
@ -119,6 +123,8 @@ const Tooltip = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const isArrowUp = positionState.verticalAlign === 'bottom';
|
const isArrowUp = positionState.verticalAlign === 'bottom';
|
||||||
|
const isArrowRight = flip === 'left' && positionState.horizontalAlign === 'end';
|
||||||
|
const isArrowLeft = flip === 'right' && positionState.horizontalAlign === 'start';
|
||||||
|
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<div
|
<div
|
||||||
|
@ -126,7 +132,9 @@ const Tooltip = ({
|
||||||
className={classNames(
|
className={classNames(
|
||||||
styles.tooltip,
|
styles.tooltip,
|
||||||
isArrowUp && styles.arrowUp,
|
isArrowUp && styles.arrowUp,
|
||||||
styles[horizontalAlign],
|
isArrowRight && styles.arrowRight,
|
||||||
|
isArrowLeft && styles.arrowLeft,
|
||||||
|
!flip && styles[horizontalAlign],
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
style={{ ...position }}
|
style={{ ...position }}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { RefObject, useCallback, useEffect, useState } from 'react';
|
import { RefObject, useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
export type VerticalAlignment = 'top' | 'bottom';
|
export type VerticalAlignment = 'top' | 'center' | 'bottom';
|
||||||
|
|
||||||
export type HorizontalAlignment = 'start' | 'center' | 'end';
|
export type HorizontalAlignment = 'start' | 'center' | 'end';
|
||||||
|
|
||||||
|
@ -28,11 +28,13 @@ const windowSafePadding = 12;
|
||||||
const selectVerticalAlignment = ({
|
const selectVerticalAlignment = ({
|
||||||
verticalAlign,
|
verticalAlign,
|
||||||
verticalTop,
|
verticalTop,
|
||||||
|
verticalCenter,
|
||||||
verticalBottom,
|
verticalBottom,
|
||||||
overlayHeight,
|
overlayHeight,
|
||||||
}: {
|
}: {
|
||||||
verticalAlign: VerticalAlignment;
|
verticalAlign: VerticalAlignment;
|
||||||
verticalTop: number;
|
verticalTop: number;
|
||||||
|
verticalCenter: number;
|
||||||
verticalBottom: number;
|
verticalBottom: number;
|
||||||
overlayHeight: number;
|
overlayHeight: number;
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -40,6 +42,8 @@ const selectVerticalAlignment = ({
|
||||||
const maxY = window.innerHeight - windowSafePadding;
|
const maxY = window.innerHeight - windowSafePadding;
|
||||||
|
|
||||||
const isTopAllowed = verticalTop >= minY;
|
const isTopAllowed = verticalTop >= minY;
|
||||||
|
const isCenterAllowed =
|
||||||
|
verticalCenter - overlayHeight / 2 >= minY && verticalCenter + overlayHeight / 2 <= maxY;
|
||||||
const isBottomAllowed = verticalBottom + overlayHeight <= maxY;
|
const isBottomAllowed = verticalBottom + overlayHeight <= maxY;
|
||||||
|
|
||||||
switch (verticalAlign) {
|
switch (verticalAlign) {
|
||||||
|
@ -52,6 +56,26 @@ const selectVerticalAlignment = ({
|
||||||
return 'bottom';
|
return 'bottom';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isCenterAllowed) {
|
||||||
|
return 'center';
|
||||||
|
}
|
||||||
|
|
||||||
|
return verticalAlign;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'center': {
|
||||||
|
if (isCenterAllowed) {
|
||||||
|
return 'center';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTopAllowed) {
|
||||||
|
return 'top';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBottomAllowed) {
|
||||||
|
return 'bottom';
|
||||||
|
}
|
||||||
|
|
||||||
return verticalAlign;
|
return verticalAlign;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +88,10 @@ const selectVerticalAlignment = ({
|
||||||
return 'top';
|
return 'top';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isCenterAllowed) {
|
||||||
|
return 'center';
|
||||||
|
}
|
||||||
|
|
||||||
return verticalAlign;
|
return verticalAlign;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,9 +198,15 @@ export default function usePosition({
|
||||||
const { scrollTop, scrollLeft } = document.documentElement;
|
const { scrollTop, scrollLeft } = document.documentElement;
|
||||||
|
|
||||||
const verticalTop = anchorRect.y - overlayRect.height + scrollTop - offset.vertical;
|
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 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 horizontalStart = anchorRect.x + scrollLeft + offset.horizontal;
|
||||||
const horizontalCenter =
|
const horizontalCenter =
|
||||||
|
@ -189,6 +223,7 @@ export default function usePosition({
|
||||||
const selectedVerticalAlign = selectVerticalAlignment({
|
const selectedVerticalAlign = selectVerticalAlignment({
|
||||||
verticalAlign,
|
verticalAlign,
|
||||||
verticalTop,
|
verticalTop,
|
||||||
|
verticalCenter,
|
||||||
verticalBottom,
|
verticalBottom,
|
||||||
overlayHeight: overlayRect.height,
|
overlayHeight: overlayRect.height,
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue