Skip to content

Commit 489e9ca

Browse files
authored
Merge pull request #269 from boostcampwm-2024/feature-fe-#268
Popover 컴포넌트에 align 옵션 추가
2 parents 096034e + 975de39 commit 489e9ca

File tree

4 files changed

+51
-9
lines changed

4 files changed

+51
-9
lines changed

apps/frontend/src/components/commons/popover/Content.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ interface ContentProps {
99
}
1010

1111
export function Content({ children, className }: ContentProps) {
12-
const { open, setOpen, triggerRef, placement, offset } = usePopover();
12+
const { open, setOpen, triggerRef, placement, offset, align } = usePopover();
1313
const contentRef = useRef<HTMLDivElement>(null);
1414
const [position, setPosition] = useState({ top: 0, left: 0 });
1515

@@ -22,10 +22,11 @@ export function Content({ children, className }: ContentProps) {
2222
contentRect,
2323
placement,
2424
offset,
25+
align,
2526
);
2627
setPosition(newPosition);
2728
}
28-
}, [open, placement, offset, triggerRef]);
29+
}, [open, placement, offset, align, triggerRef]);
2930

3031
useEffect(() => {
3132
const handleClickOutside = (e: MouseEvent) => {

apps/frontend/src/components/commons/popover/index.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
import { useRef, useState } from "react";
22
import { Content } from "./Content";
33
import { Trigger } from "./Trigger";
4-
import { PopoverContext, Placement, Offset } from "@/hooks/usePopover";
4+
import {
5+
PopoverContext,
6+
Placement,
7+
Offset,
8+
Alignment,
9+
} from "@/hooks/usePopover";
510

611
interface PopoverProps {
712
children: React.ReactNode;
813
placement?: Placement;
914
offset?: Partial<Offset>;
15+
align?: Alignment;
1016
}
1117

1218
function Popover({
1319
children,
1420
placement = "bottom",
21+
align = "center",
1522
offset = { x: 0, y: 0 },
1623
}: PopoverProps) {
1724
const [open, setOpen] = useState(false);
@@ -30,6 +37,7 @@ function Popover({
3037
triggerRef,
3138
placement,
3239
offset: fullOffset,
40+
align,
3341
}}
3442
>
3543
{children}

apps/frontend/src/hooks/usePopover.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createContext, useContext } from "react";
22

33
export type Placement = "top" | "right" | "bottom" | "left";
4+
export type Alignment = "start" | "center" | "end";
45

56
export interface Offset {
67
x: number;
@@ -13,6 +14,7 @@ export interface PopoverContextType {
1314
triggerRef: React.RefObject<HTMLDivElement>;
1415
placement: Placement;
1516
offset: Offset;
17+
align: Alignment;
1618
}
1719

1820
export const PopoverContext = createContext<PopoverContextType | null>(null);

apps/frontend/src/lib/getPopoverPosition.ts

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Placement, Offset } from "@/hooks/usePopover";
1+
import { Placement, Offset, Alignment } from "@/hooks/usePopover";
22

33
export interface Position {
44
top: number;
@@ -9,45 +9,75 @@ interface PositionConfig {
99
triggerRect: DOMRect;
1010
contentRect: DOMRect;
1111
offset: Offset;
12+
align: Alignment;
1213
}
1314

15+
const alignmentMap: Record<
16+
Alignment,
17+
(triggerSize: number, contentSize: number) => number
18+
> = {
19+
start: () => 0,
20+
center: (triggerSize, contentSize) => (triggerSize - contentSize) / 2,
21+
end: (triggerSize, contentSize) => triggerSize - contentSize,
22+
};
23+
24+
const getAlignment = (
25+
triggerSize: number,
26+
contentSize: number,
27+
align: Alignment,
28+
): number => {
29+
return alignmentMap[align](triggerSize, contentSize);
30+
};
31+
1432
const getTopPosition = ({
1533
triggerRect,
1634
contentRect,
1735
offset,
36+
align,
1837
}: PositionConfig): Position => ({
1938
top: triggerRect.top - contentRect.height - offset.y,
2039
left:
21-
triggerRect.left + (triggerRect.width - contentRect.width) / 2 + offset.x,
40+
triggerRect.left +
41+
getAlignment(triggerRect.width, contentRect.width, align) +
42+
offset.x,
2243
});
2344

2445
const getRightPosition = ({
2546
triggerRect,
2647
contentRect,
2748
offset,
49+
align,
2850
}: PositionConfig): Position => ({
2951
top:
30-
triggerRect.top + (triggerRect.height - contentRect.height) / 2 + offset.y,
52+
triggerRect.top +
53+
getAlignment(triggerRect.height, contentRect.height, align) +
54+
offset.y,
3155
left: triggerRect.right + offset.x,
3256
});
3357

3458
const getBottomPosition = ({
3559
triggerRect,
3660
contentRect,
3761
offset,
62+
align,
3863
}: PositionConfig): Position => ({
3964
top: triggerRect.bottom + offset.y,
4065
left:
41-
triggerRect.left + (triggerRect.width - contentRect.width) / 2 + offset.x,
66+
triggerRect.left +
67+
getAlignment(triggerRect.width, contentRect.width, align) +
68+
offset.x,
4269
});
4370

4471
const getLeftPosition = ({
4572
triggerRect,
4673
contentRect,
4774
offset,
75+
align,
4876
}: PositionConfig): Position => ({
4977
top:
50-
triggerRect.top + (triggerRect.height - contentRect.height) / 2 + offset.y,
78+
triggerRect.top +
79+
getAlignment(triggerRect.height, contentRect.height, align) +
80+
offset.y,
5181
left: triggerRect.left - contentRect.width - offset.x,
5282
});
5383

@@ -63,7 +93,8 @@ export function getPosition(
6393
contentRect: DOMRect,
6494
placement: Placement,
6595
offset: Offset,
96+
align: Alignment,
6697
): Position {
67-
const config = { triggerRect, contentRect, offset };
98+
const config = { triggerRect, contentRect, offset, align };
6899
return positionMap[placement](config);
69100
}

0 commit comments

Comments
 (0)