From 2f1cc8f4ed83d87f5db98ca2fab7971710b9868a Mon Sep 17 00:00:00 2001 From: Kim DoKyun <51306225+jasper200207@users.noreply.github.com> Date: Mon, 15 Jul 2024 15:10:12 +0900 Subject: [PATCH] Feature/#284 participant menu (#285) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat : ParticipantMenuProps & ParticipantItemProps * feat : ParticipantItem * feat : ParticipantMenu * refactor : 리뷰반영 - includeMembers, excludeMembers optional - excludeMember가 존재할때만 Divider - #284 --- .../ParticipantMenu/ParticipantItem/index.tsx | 79 +++++++++++++ src/components/ParticipantMenu/index.tsx | 111 ++++++++++++++++++ src/components/ParticipantMenu/types.ts | 24 ++++ 3 files changed, 214 insertions(+) create mode 100644 src/components/ParticipantMenu/ParticipantItem/index.tsx create mode 100644 src/components/ParticipantMenu/index.tsx create mode 100644 src/components/ParticipantMenu/types.ts diff --git a/src/components/ParticipantMenu/ParticipantItem/index.tsx b/src/components/ParticipantMenu/ParticipantItem/index.tsx new file mode 100644 index 0000000..afb51c3 --- /dev/null +++ b/src/components/ParticipantMenu/ParticipantItem/index.tsx @@ -0,0 +1,79 @@ +import { Avatar, Flex, IconButton, Text } from '@chakra-ui/react'; +import { RiAddLine, RiCloseFill, RiVipCrownLine } from 'react-icons/ri'; + +import { ParticipantItemProps } from '@/components/ParticipantMenu/types'; +import colors from '@/theme/foundations/colors'; + +const ParticipantItem = ({ member, type, onAdd, onMandateLeader, onRemove }: ParticipantItemProps) => { + const handleDeleteMember = () => { + onRemove(member); + }; + + const handleMandateLeader = () => { + onMandateLeader(member); + }; + + const handleAddMember = () => { + onAdd(member); + }; + + return ( + + + + {member.name} + + {type === 'INCLUDE' && ( + + } + onClick={handleDeleteMember} + size="icon_sm" + /> + } + onClick={handleMandateLeader} + size="icon_sm" + /> + + )} + {type === 'EXCLUDE' && ( + + } + onClick={handleAddMember} + size="icon_sm" + /> + + )} + + ); +}; + +export default ParticipantItem; diff --git a/src/components/ParticipantMenu/index.tsx b/src/components/ParticipantMenu/index.tsx new file mode 100644 index 0000000..e57a5f0 --- /dev/null +++ b/src/components/ParticipantMenu/index.tsx @@ -0,0 +1,111 @@ +/* eslint-disable react/jsx-props-no-spreading */ +import { Divider, Flex, Input } from '@chakra-ui/react'; +import { useEffect, useRef, useState } from 'react'; +import { BiSearch } from 'react-icons/bi'; + +import ParticipantItem from '@/components/ParticipantMenu/ParticipantItem'; +import { ParticipantMenuProps } from '@/components/ParticipantMenu/types'; +import { Member } from '@/types'; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const defaultFunction = (member: Member) => {}; + +const ParticipantMenu = ({ + leader, + includeMembers = [], + excludeMembers = [], + children, + isOpen, + setIsOpen, + onRemove = defaultFunction, + onAdd = defaultFunction, + onMandateLeader = defaultFunction, + ...flexProps +}: ParticipantMenuProps) => { + const [search, setSearch] = useState(''); + const menuRef = useRef(null); + + const searchedLeader = leader?.name.includes(search) ? leader : null; + const searchedIncludeMember = includeMembers.filter((member) => member.name.includes(search)); + const searchedExcludeMember = excludeMembers.filter((member) => member.name.includes(search)); + + useEffect(() => { + const handleOutsideClose = (e: MouseEvent) => { + if (isOpen && !menuRef.current?.contains(e.target as Node)) setIsOpen(false); + }; + document.addEventListener('click', handleOutsideClose); + + return () => document.removeEventListener('click', handleOutsideClose); + }, [isOpen, setIsOpen]); + + return ( + + { + setIsOpen((prev) => !prev); + }} + > + {children} + + + ); +}; + +export default ParticipantMenu; diff --git a/src/components/ParticipantMenu/types.ts b/src/components/ParticipantMenu/types.ts new file mode 100644 index 0000000..9bdd990 --- /dev/null +++ b/src/components/ParticipantMenu/types.ts @@ -0,0 +1,24 @@ +import { FlexProps } from '@chakra-ui/react'; +import { ReactNode } from 'react'; + +import { Member } from '@/types'; + +export interface ParticipantMenuProps extends FlexProps { + leader?: Member; + includeMembers?: Member[]; + excludeMembers?: Member[]; + children: ReactNode; + isOpen: boolean; + setIsOpen: React.Dispatch>; + onRemove?: (member: Member) => void; + onAdd?: (member: Member) => void; + onMandateLeader?: (member: Member) => void; +} + +export interface ParticipantItemProps { + member: Member; + type: 'LEADER' | 'INCLUDE' | 'EXCLUDE'; + onRemove: (member: Member) => void; + onAdd: (member: Member) => void; + onMandateLeader: (member: Member) => void; +}