-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #238 from boostcampwm-2024/Feature/#219_워크스페이스_선택_…
…모달창_구현 Feature/#219 워크스페이스 선택 모달창 구현
- Loading branch information
Showing
23 changed files
with
784 additions
and
231 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 27 additions & 3 deletions
30
client/src/components/sidebar/components/menuButton/MenuButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,40 @@ | ||
import { useState, useEffect } from "react"; | ||
import { useUserInfo } from "@stores/useUserStore"; | ||
import { menuItemWrapper, textBox } from "./MenuButton.style"; | ||
import { menuItemWrapper, textBox, menuButtonContainer } from "./MenuButton.style"; | ||
import { MenuIcon } from "./components/MenuIcon"; | ||
import { WorkspaceSelectModal } from "./components/WorkspaceSelectModal"; | ||
|
||
export const MenuButton = () => { | ||
const { name } = useUserInfo(); | ||
const [isOpen, setIsOpen] = useState(false); | ||
|
||
const handleMenuClick = () => { | ||
setIsOpen((prev) => !prev); // 토글 형태로 변경 | ||
}; | ||
|
||
// 모달 외부 클릭시 닫기 처리를 위한 함수 | ||
const handleClickOutside = (e: MouseEvent) => { | ||
const target = e.target as HTMLElement; | ||
if (!target.closest(`.menu_button_container`)) { | ||
setIsOpen(false); | ||
} | ||
}; | ||
|
||
// 외부 클릭 이벤트 리스너 등록 | ||
useEffect(() => { | ||
document.addEventListener("mousedown", handleClickOutside); | ||
return () => { | ||
document.removeEventListener("mousedown", handleClickOutside); | ||
}; | ||
}, []); | ||
|
||
return ( | ||
<> | ||
<button className={`${menuButtonContainer} menu_button_container`} onClick={handleMenuClick}> | ||
<button className={menuItemWrapper}> | ||
<MenuIcon /> | ||
<p className={textBox}>{name ?? "Nocta"}</p> | ||
</button> | ||
</> | ||
<WorkspaceSelectModal isOpen={isOpen} userName={name} /> | ||
</button> | ||
); | ||
}; |
29 changes: 29 additions & 0 deletions
29
client/src/components/sidebar/components/menuButton/components/WorkspaceSelectModal.style.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { css, cx } from "@styled-system/css"; | ||
import { glassContainer } from "@styled-system/recipes"; | ||
|
||
export const workspaceListContainer = css({ | ||
display: "flex", | ||
gap: "sm", | ||
flexDirection: "column", | ||
padding: "md", | ||
}); | ||
|
||
export const workspaceModalContainer = cx( | ||
glassContainer({ | ||
border: "md", | ||
borderRadius: "bottom", | ||
background: "none", | ||
boxShadow: "bottom", | ||
}), | ||
css({ | ||
display: "flex", | ||
}), | ||
); | ||
|
||
export const textBox = css({ | ||
padding: "lg", | ||
color: "gray.500", | ||
textAlign: "center", | ||
fontSize: "md", | ||
whiteSpace: "pre-line", | ||
}); |
57 changes: 57 additions & 0 deletions
57
client/src/components/sidebar/components/menuButton/components/WorkspaceSelectModal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { motion } from "framer-motion"; | ||
import { useRef } from "react"; | ||
import { SIDE_BAR } from "@constants/size"; | ||
import { useSocketStore } from "@src/stores/useSocketStore"; | ||
import { | ||
workspaceListContainer, | ||
workspaceModalContainer, | ||
textBox, | ||
} from "./WorkspaceSelectModal.style"; | ||
import { WorkspaceSelectItem } from "./components/WorkspaceSelectItem"; | ||
|
||
interface WorkspaceSelectModalProps { | ||
isOpen: boolean; | ||
userName: string | null; | ||
} | ||
|
||
export const WorkspaceSelectModal = ({ isOpen, userName }: WorkspaceSelectModalProps) => { | ||
const modalRef = useRef<HTMLDivElement>(null); | ||
const { availableWorkspaces } = useSocketStore(); // 소켓 스토어에서 직접 워크스페이스 목록 가져오기 | ||
|
||
const informText = userName | ||
? availableWorkspaces.length > 0 | ||
? "" | ||
: "접속할 수 있는 워크스페이스가 없습니다." | ||
: `다른 워크스페이스 기능은\n 회원전용 입니다`; | ||
return ( | ||
<motion.div | ||
ref={modalRef} | ||
className={workspaceModalContainer} | ||
initial={{ opacity: 0, y: -20 }} | ||
animate={{ | ||
opacity: isOpen ? 1 : 0, | ||
y: isOpen ? 0 : -20, | ||
}} | ||
transition={{ duration: 0.2 }} | ||
style={{ | ||
position: "absolute", | ||
top: "calc(100% + 4px)", | ||
left: -1, | ||
width: SIDE_BAR.WIDTH, | ||
zIndex: 20, | ||
pointerEvents: isOpen ? "auto" : "none", | ||
display: isOpen ? "block" : "none", | ||
}} | ||
> | ||
<div className={workspaceListContainer}> | ||
{userName && availableWorkspaces.length > 0 ? ( | ||
availableWorkspaces.map((workspace) => ( | ||
<WorkspaceSelectItem key={workspace.id} userName={userName} {...workspace} /> | ||
)) | ||
) : ( | ||
<p className={textBox}>{informText}</p> | ||
)} | ||
</div> | ||
</motion.div> | ||
); | ||
}; |
48 changes: 48 additions & 0 deletions
48
...ponents/sidebar/components/menuButton/components/components/WorkspaceSelectItem.style.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// WorkspaceSelectItem.style.ts | ||
import { css } from "@styled-system/css"; | ||
|
||
export const itemContainer = css({ | ||
display: "flex", | ||
justifyContent: "space-between", | ||
alignItems: "center", | ||
padding: "md", | ||
cursor: "pointer", | ||
_hover: { backgroundColor: "gray.100" }, | ||
}); | ||
|
||
export const itemContent = css({ | ||
display: "flex", | ||
gap: "2", | ||
alignItems: "center", | ||
}); | ||
|
||
export const itemIcon = css({ | ||
display: "flex", | ||
justifyContent: "center", | ||
alignItems: "center", | ||
borderRadius: "xl", | ||
width: "8", | ||
height: "8", | ||
fontSize: "sm", | ||
backgroundColor: "gray.200", | ||
}); | ||
|
||
export const itemInfo = css({ | ||
display: "flex", | ||
flexDirection: "column", | ||
}); | ||
|
||
export const itemName = css({ | ||
fontSize: "sm", | ||
fontWeight: "medium", | ||
}); | ||
|
||
export const itemRole = css({ | ||
color: "gray.500", | ||
fontSize: "xs", | ||
}); | ||
|
||
export const itemMemberCount = css({ | ||
color: "gray.500", | ||
fontSize: "xs", | ||
}); |
38 changes: 38 additions & 0 deletions
38
...rc/components/sidebar/components/menuButton/components/components/WorkspaceSelectItem.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { WorkspaceListItem } from "@noctaCrdt/Interfaces"; // 이전에 만든 인터페이스 import | ||
import { useSocketStore } from "@src/stores/useSocketStore"; | ||
import { useUserInfo } from "@src/stores/useUserStore"; | ||
import { | ||
itemContainer, | ||
itemContent, | ||
itemIcon, | ||
itemInfo, | ||
itemMemberCount, | ||
itemName, | ||
itemRole, | ||
} from "./WorkspaceSelectItem.style"; | ||
|
||
interface WorkspaceSelectItemProps extends WorkspaceListItem { | ||
userName: string; | ||
} | ||
|
||
export const WorkspaceSelectItem = ({ id, name, role, memberCount }: WorkspaceSelectItemProps) => { | ||
const { userId } = useUserInfo(); | ||
const switchWorkspace = useSocketStore((state) => state.switchWorkspace); | ||
const handleClick = () => { | ||
switchWorkspace(userId, id); | ||
}; | ||
|
||
return ( | ||
<button className={itemContainer} onClick={handleClick}> | ||
<div className={itemContent}> | ||
<div className={itemIcon}>{name.charAt(0)}</div> | ||
<div className={itemInfo}> | ||
<span className={itemName}>{name}</span> | ||
<span className={itemMemberCount}>{role}</span> | ||
</div> | ||
</div> | ||
|
||
{memberCount !== undefined && <span className={itemRole}>접속자수 : {memberCount} 명 </span>} | ||
</button> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.