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%; top: 0%;
} }
&.arrowRight::after {
top: 50%;
left: 100%;
}
&.arrowLeft::after {
top: 50%;
left: 0%;
}
&.start::after { &.start::after {
left: _.unit(10); left: _.unit(10);
} }

View file

@ -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 }}

View file

@ -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,
}); });