Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<body>
<div id="root"></div>
<div id="dropdown"></div>
<div id="popover"></div>
<div id="modal"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
Expand Down
5 changes: 4 additions & 1 deletion src/app.jsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -13,7 +14,9 @@ import TestPage from "./pages/test-page";
function Provider({ children }) {
return (
<ModalProvider>
<DropdownProvider>{children}</DropdownProvider>
<PopoverProvider>
<DropdownProvider>{children}</DropdownProvider>
</PopoverProvider>
</ModalProvider>
);
}
Expand Down
6 changes: 6 additions & 0 deletions src/components/popover/popover-alignment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const POPOVER_ALIGNMENT = Object.freeze({
left: "left",
right: "right",
});

export default POPOVER_ALIGNMENT;
5 changes: 5 additions & 0 deletions src/components/popover/popover-context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createContext } from "react";

const PopoverContext = createContext();

export default PopoverContext;
10 changes: 10 additions & 0 deletions src/components/popover/popover-provider.jsx
Original file line number Diff line number Diff line change
@@ -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 <PopoverContext value={value}>{children}</PopoverContext>;
}

export default PopoverProvider;
39 changes: 39 additions & 0 deletions src/components/popover/popover.jsx
Original file line number Diff line number Diff line change
@@ -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 && (
<PopoverPortal>
<Container onClick={onClose}>
<StyledPopover $position={position}>{children}</StyledPopover>
</Container>
</PopoverPortal>
)
);
}

export default Popover;
68 changes: 68 additions & 0 deletions src/hooks/use-popover.jsx
Original file line number Diff line number Diff line change
@@ -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 };
51 changes: 47 additions & 4 deletions src/pages/test-page.jsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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)`
Expand Down Expand Up @@ -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 (
<div
style={{
Expand Down Expand Up @@ -210,6 +235,27 @@ function TestPage() {
/>
)}
</div>
<div style={{ display: "flex", gap: "16px" }}>
<PrimaryButton
size={BUTTON_SIZE.small}
title="Show Popover on Left"
onClick={handlePopoverLeftClick}
ref={popoverLeftRef}
/>
<PrimaryButton
size={BUTTON_SIZE.small}
title="Show Popover on Right"
onClick={handlePopoverRightClick}
ref={popoverRightRef}
/>
<Popover
isOpen={showsPopover}
onClose={closePopover}
position={popoverPosition}
>
<h1>This is Popover.</h1>
</Popover>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
<div style={{ display: "flex", alignItems: "center", gap: "16px" }}>
<Avatar size={AVATAR_SIZE.large} />
Expand All @@ -224,9 +270,6 @@ function TestPage() {
size={AVATAR_SIZE.extraSmall}
/>
</div>
<div
style={{ display: "flex", alignItems: "center", gap: "16px" }}
></div>
</div>
</div>
);
Expand Down