diff --git a/index.html b/index.html
index 80aec14..07a9876 100644
--- a/index.html
+++ b/index.html
@@ -16,6 +16,7 @@
+
diff --git a/src/app.jsx b/src/app.jsx
index d3303ef..0052846 100644
--- a/src/app.jsx
+++ b/src/app.jsx
@@ -1,5 +1,6 @@
import { BrowserRouter, Route, Routes } from "react-router";
import ModalProvider from "./components/modal/modal-provider";
+import PopoverProvider from "./components/popover/popover-provider";
import DropdownProvider from "./components/text-field/dropdown-input/dropdown-provider";
import ContentLayout from "./layouts/content-layout";
import OnboardingLayout from "./layouts/onboarding-layout";
@@ -13,7 +14,9 @@ import TestPage from "./pages/test-page";
function Provider({ children }) {
return (
- {children}
+
+ {children}
+
);
}
diff --git a/src/components/popover/popover-alignment.js b/src/components/popover/popover-alignment.js
new file mode 100644
index 0000000..a08fdf5
--- /dev/null
+++ b/src/components/popover/popover-alignment.js
@@ -0,0 +1,6 @@
+const POPOVER_ALIGNMENT = Object.freeze({
+ left: "left",
+ right: "right",
+});
+
+export default POPOVER_ALIGNMENT;
diff --git a/src/components/popover/popover-context.js b/src/components/popover/popover-context.js
new file mode 100644
index 0000000..06b8815
--- /dev/null
+++ b/src/components/popover/popover-context.js
@@ -0,0 +1,5 @@
+import { createContext } from "react";
+
+const PopoverContext = createContext();
+
+export default PopoverContext;
diff --git a/src/components/popover/popover-provider.jsx b/src/components/popover/popover-provider.jsx
new file mode 100644
index 0000000..4529615
--- /dev/null
+++ b/src/components/popover/popover-provider.jsx
@@ -0,0 +1,10 @@
+import { useState } from "react";
+import PopoverContext from "./popover-context";
+
+function PopoverProvider({ children }) {
+ const [showsPopover, setShowsPopover] = useState(false);
+ const value = { showsPopover, setShowsPopover };
+ return {children};
+}
+
+export default PopoverProvider;
diff --git a/src/components/popover/popover.jsx b/src/components/popover/popover.jsx
new file mode 100644
index 0000000..2258612
--- /dev/null
+++ b/src/components/popover/popover.jsx
@@ -0,0 +1,39 @@
+import { createPortal } from "react-dom";
+import styled from "styled-components";
+
+const Container = styled.div`
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+`;
+
+function PopoverPortal({ children }) {
+ return createPortal(children, document.getElementById("popover"));
+}
+
+const StyledPopover = styled.div`
+ position: absolute;
+ top: ${({ $position }) => $position.top}px;
+ ${({ $position }) => ($position.left ? `left: ${$position.left}px` : "")};
+ ${({ $position }) => ($position.right ? `right: ${$position.right}px` : "")};
+ border-radius: 8px;
+ border: 1px solid #b6b6b6;
+ background-color: white;
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08);
+`;
+
+function Popover({ isOpen, onClose, position, children }) {
+ return (
+ isOpen && (
+
+
+ {children}
+
+
+ )
+ );
+}
+
+export default Popover;
diff --git a/src/hooks/use-popover.jsx b/src/hooks/use-popover.jsx
new file mode 100644
index 0000000..70cea0d
--- /dev/null
+++ b/src/hooks/use-popover.jsx
@@ -0,0 +1,68 @@
+import { useContext, useState, useEffect } from "react";
+import POPOVER_ALIGNMENT from "../components/popover/popover-alignment";
+import PopoverContext from "../components/popover/popover-context";
+
+function calculatePopoverPosition(target, alignment) {
+ if (!target) {
+ return { top: 0, left: 0, right: 0 };
+ }
+
+ const targetRect = target.getBoundingClientRect();
+ const position = {
+ top: targetRect.bottom + 8,
+ };
+
+ switch (alignment) {
+ case POPOVER_ALIGNMENT.right:
+ position.right = window.innerWidth - targetRect.right;
+ break;
+ default:
+ position.left = targetRect.left;
+ break;
+ }
+
+ return position;
+}
+
+function usePopover() {
+ const { showsPopover, setShowsPopover } = useContext(PopoverContext);
+ const [popoverPosition, setPopoverPosition] = useState();
+ const [target, setTarget] = useState();
+ const [alignment, setAlignment] = useState(POPOVER_ALIGNMENT.left);
+
+ const openPopopver = ({ target, alignment }) => {
+ updatePopoverPosition(target, alignment);
+ setTarget(target);
+ setAlignment(alignment);
+ setShowsPopover(true);
+ };
+
+ const closePopover = () => {
+ setShowsPopover(false);
+ };
+
+ const updatePopoverPosition = (target, alignment) => {
+ const position = calculatePopoverPosition(target, alignment);
+ setPopoverPosition(position);
+ };
+
+ useEffect(() => {
+ if (!showsPopover) return;
+
+ function handleWindowResize() {
+ updatePopoverPosition(target, alignment);
+ }
+
+ window.addEventListener("resize", handleWindowResize);
+ return () => window.removeEventListener("resize", handleWindowResize);
+ }, [showsPopover, target, alignment]);
+
+ return {
+ popoverPosition,
+ showsPopover,
+ openPopopver,
+ closePopover,
+ };
+}
+
+export { usePopover };
diff --git a/src/pages/test-page.jsx b/src/pages/test-page.jsx
index eae2ac7..fcaad0f 100644
--- a/src/pages/test-page.jsx
+++ b/src/pages/test-page.jsx
@@ -1,4 +1,4 @@
-import { useState } from "react";
+import { useRef, useState } from "react";
import styled from "styled-components";
import smileAddImage from "../assets/ic-face-smile-add.svg";
import Avatar from "../components/avatar/avatar";
@@ -17,10 +17,13 @@ import BUTTON_SIZE from "../components/button/button-size";
import ToggleButton from "../components/button/toggle-button";
import Header from "../components/header/header";
import Modal from "../components/modal/modal";
+import Popover from "../components/popover/popover";
+import POPOVER_ALIGNMENT from "../components/popover/popover-alignment";
import TextField from "../components/text-field/text-field";
import TEXT_FIELD_TYPE from "../components/text-field/text-field-type";
import Toast from "../components/toast/toast";
import { useModal } from "../hooks/use-modal";
+import { usePopover } from "../hooks/use-popover";
import { useToast } from "../hooks/use-toast";
const OutlinedHeader = styled(Header)`
@@ -51,6 +54,28 @@ function TestPage() {
const { showsModal, setShowsModal } = useModal();
const handleModalClick = () => setShowsModal(true);
+<<<<<<< HEAD
+ /* Popover */
+ const { popoverPosition, showsPopover, openPopopver, closePopover } =
+ usePopover();
+ const popoverLeftRef = useRef();
+ const popoverRightRef = useRef();
+
+ const handlePopoverLeftClick = () => {
+ openPopopver({
+ target: popoverLeftRef.current,
+ alignment: POPOVER_ALIGNMENT.left,
+ });
+ };
+ const handlePopoverRightClick = () => {
+ openPopopver({
+ target: popoverRightRef.current,
+ alignment: POPOVER_ALIGNMENT.right,
+ });
+ };
+
+=======
+>>>>>>> upstream/develop
return (
)}
+
+
+
+
+ This is Popover.
+
+
@@ -224,9 +270,6 @@ function TestPage() {
size={AVATAR_SIZE.extraSmall}
/>
-
);