Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add List - Notification component #39

Merged
merged 9 commits into from
Aug 30, 2024
41 changes: 39 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Button from "@/components/Button"
import ToastMessage from "@/components/ToastMessage"
import Chip from "@components/Chip"
import Modal from "@/components/Modal"
import Notification from "@/components/List/Notification"
import LinkCalendar from "@/components/LinkCalendar"
import Video from "@/components/Video"

Expand All @@ -23,13 +24,13 @@ import LogoutSvg from "@assets/icons/out.svg"
import Withdraw from "@assets/icons/withdraw.svg"
import Youtube from "@assets/icons/youtube.svg"
import Link from "@assets/icons/link.svg"

import SpacewakTextLogo from "@assets/logo/spacewak-text-logo.svg"
import WaktaplayTextLogo from "@assets/logo/waktaplay-text-logo.svg"

import ChunsikSVG from "@assets/icons/members/gwakchunsik.svg"
import WaktaverseSVG from "@assets/icons/group/waktaverse.svg"

import { IElementProps } from "@components/List/types"

// TODO : 아래 더미데이터들은 원하는 내용으로 바꾸시면 되고 unit은 지우셔도 무방합니다.
const TABS = [
{
Expand Down Expand Up @@ -100,6 +101,36 @@ const UNITS = [
},
]

const notificationSample: IElementProps[] = [
{
icon: SpacewakTextLogo,
content: "왁타버스 시작",
view: false,
createdAt: "2시간 전",
category: "게시판",
type: "favorite",
time: null,
},
{
icon: SpacewakTextLogo,
content: "왁타버스 1 이렇게 길게 만들어 보면 어떻게 될까요 한번 가시보죠",
view: true,
createdAt: "2시간 전",
category: "왁타플레이",
time: "오후 12시 45분",
type: "calendar",
},
{
icon: SpacewakTextLogo,
content: '"왁타버스" 님이 "스페이스왁"을 친구 추가 했습니다.',
view: true,
createdAt: "2시간 전",
category: "왁타플레이",
link: "https://www.youtube.com/@waktaverse",
type: "favorite",
},
]

const App = () => {
//해당 selectedTabIndex,isShowedMore 두개의 state 들은 tabs UI 와 사용되는 state 입니다.
const [selectedTabIndex, setSelectedTabIndex] = useState(0)
Expand Down Expand Up @@ -405,6 +436,12 @@ const App = () => {
<LinkCalendar icon={Youtube} onClick={() => alert("link click")} />
<LinkCalendar icon={Link} onClick={() => alert("link click")} />
</section>

<section>
<h3>Notification</h3>

<Notification itemList={notificationSample} />
</section>
</main>

<ToastMessage message={message} setMessage={setMessage} />
Expand Down
3 changes: 3 additions & 0 deletions src/assets/icons/popup.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions src/assets/icons/schedule_calendar.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
156 changes: 156 additions & 0 deletions src/components/List/Element.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import styled from "styled-components"

import PopupSVG from "@/assets/icons/popup.svg"
import Calender from "@/assets/icons/schedule_calendar.svg"

import { IElementProps } from "./types"

const ElementWrapper = styled.div`
height: 40px;
align-items: center;
display: flex;
flex-direction: row;
gap: 18px;
padding: 22px 18px;
overflow: hidden;

&:hover {
background: rgba(255, 255, 255, 0.1);
}

&:active {
background: rgba(255, 255, 255, 0.2);
}
`

const IconWrapper = styled.div`
flex: 0 0 40px;
height: 40px;
position: relative;
`

const DotWrapper = styled.div`
width: 4px;
height: 4px;
background: #47f998;
border-radius: 50%;
position: absolute;
top: 0;
right: 0;
z-index: 10;
`

const IconBgWrapper = styled.div`
background: #303133;
border-radius: 50%;
height: 40px;

img {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
`

const BodyWrapper = styled.div`
flex: 0 0 260px;
display: flex;
flex-direction: column;
gap: 12px;
height: 40px;
overflow: hidden;
`

const InformationsWrapper = styled.div`
display: flex;
flex: 0 0 12px;
flex-direction: row;
justify-content: space-between;
font-weight: 500;

.category {
flex: 0 0 fit-content;
font-size: 10px;
color: #d3d5db;
}

.createdAt {
flex: 0 0 fit-content;
font-size: 9px;
color: #b4b8c2;
}
`

const DescriptionsWrapper = styled.div`
display: flex;
flex-direction: row;
gap: 10px;
font-weight: 600;
justify-content: flex-start;
height: 18px;
font-size: 16px;

.time {
color: #95ffad;
flex: 0 0 fit-content;
}

.content {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
margin: 0;
font-weight: 600;
font-size: 16px;
}

.link {
flex: 0 0 18px;
&:hover {
cursor: pointer;
}
}
`

const NotificationElement = ({ icon, content, view, createdAt, category, type, time, link }: IElementProps) => {
// link icon click event - move to link
const onClickLink = () => {
location.href = link
}

return (
<ElementWrapper>
<IconWrapper>
{!view && <DotWrapper />}
<IconBgWrapper>
{type === "calendar" && <img className="calendar" src={Calender} width={24} height={24} />}
{type === "favorite" && <img src={icon} width={40} height={40} />}
</IconBgWrapper>
</IconWrapper>

<BodyWrapper>
<InformationsWrapper>
<div className="category" title={category}>
{category}
</div>
<div className="createdAt">{createdAt}</div>
</InformationsWrapper>

<DescriptionsWrapper>
{/* type이 "calendar일 때 <시간>이 보임" */}
{type === "calendar" && <div className="time">{time}</div>}
<div className="content" title={content}>
{content}
</div>
{/* [type]이 "favorite"이고, [link]값이 있으면 바로가기 아이콘이 보임 */}
{link && type === "favorite" && (
<img className="link" src={PopupSVG} width={18} height={18} onClick={onClickLink} />
)}
</DescriptionsWrapper>
</BodyWrapper>
</ElementWrapper>
)
}

export default NotificationElement
45 changes: 45 additions & 0 deletions src/components/List/Notification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import styled from "styled-components"
import NotificationElement from "./Element"

const NotificationWrapper = styled.div`
width: 354px;
display: flex;
flex-direction: column;
`

interface IElementProps {
itemList: {
icon: string
content: string
view: boolean
createdAt: string
category: string
type: "calendar" | "favorite"
time?: string | null
link?: string
}[]
}

const Notification = ({ itemList }: IElementProps) => {
return (
<NotificationWrapper>
{itemList.map((item, i) => {
return (
<NotificationElement
key={`notification-${i}`}
icon={item.icon}
content={item.content}
view={item.view}
createdAt={item.createdAt}
category={item.category}
time={item.time}
type={item.type}
link={item.link}
/>
)
})}
</NotificationWrapper>
)
}

export default Notification
10 changes: 10 additions & 0 deletions src/components/List/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface IElementProps {
icon: string
content: string
view: boolean
createdAt: string
category: string
type: "calendar" | "favorite"
time?: string | null
link?: string
}
29 changes: 16 additions & 13 deletions src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
export { default as Button } from "./Button";
export { default as CheckBox } from "./CheckBox";
export { default as Button } from "./Button"
export { default as CheckBox } from "./CheckBox"

export { default as Card } from "./Card";
export type { ICardLink } from "./Card/types";
export { default as Card } from "./Card"
export type { ICardLink } from "./Card/types"

export { default as Chip } from "./Chip";
export { default as Chip } from "./Chip"
export type { IChipBaseProps, IChipMediumProps, IChipSmallProps } from "./Chip/types"

export { default as Filter } from "./Filter";
export { default as Footer } from "./Footer";
export { default as Filter } from "./Filter"
export { default as Footer } from "./Footer"

export { default as Menu } from "./Menu";
export { default as Tabs } from "./Tabs";
export { default as Video } from "./Video";
export { default as LinkCalendar } from "./LinkCalendar";
export { default as Menu } from "./Menu"
export { default as Tabs } from "./Tabs"
export { default as Video } from "./Video"
export { default as LinkCalendar } from "./LinkCalendar"

export { default as Modal } from "./Modal";
export { default as ToastMessage } from "./ToastMessage";
export { default as Modal } from "./Modal"
export { default as ToastMessage } from "./ToastMessage"

export { default as ListNotification } from "./List/Notification"
export type { IElementProps } from "./List/types"