-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d8f1a5d
commit a8fd6db
Showing
22 changed files
with
924 additions
and
4 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { Emotions } from '@/shared/model/EmotionEnum'; | ||
|
||
export interface Reaction { | ||
emotion: Emotions; | ||
reactionCnt: number; | ||
isClicked: boolean; | ||
} |
77 changes: 77 additions & 0 deletions
77
src/entities/ReactionButtonContainer/ui/ReactionButtonContainer.stories.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,77 @@ | ||
import React from 'react'; | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
import ReactionButtonContainer from './ReactionButtonContainer'; | ||
import { Emotions } from '../../../shared/model/EmotionEnum'; | ||
|
||
const meta: Meta<typeof ReactionButtonContainer> = { | ||
component: ReactionButtonContainer, | ||
title: 'entities/ReactionButtonContainer', | ||
tags: ['autodocs'], | ||
argTypes: { | ||
isHorizontal: { | ||
description: '버튼 배열 방식: true일 경우 가로 배열, false일 경우 세로 배열입니다.', | ||
}, | ||
isAddBtnVisible: { | ||
description: '이모티콘 추가 버튼을 추가하세요 (기본 : false)', | ||
}, | ||
reactions: { | ||
description: '감정 버튼 목록입니다. 각 버튼의 감정, 반응 수, 클릭 상태를 포함합니다.', | ||
}, | ||
}, | ||
}; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof ReactionButtonContainer>; | ||
|
||
export const Default: Story = { | ||
args: { | ||
isHorizontal: true, | ||
isAddBtnVisible: true, | ||
reactions: [ | ||
{ emotion: Emotions.Happy, reactionCnt: 10, isClicked: false }, | ||
{ emotion: Emotions.Angry, reactionCnt: 5, isClicked: false }, | ||
{ emotion: Emotions.Annoyed, reactionCnt: 8, isClicked: false }, | ||
{ emotion: Emotions.Awkward, reactionCnt: 10, isClicked: false }, | ||
{ emotion: Emotions.Blank, reactionCnt: 5, isClicked: false }, | ||
{ emotion: Emotions.Comfortable, reactionCnt: 8, isClicked: false }, | ||
{ emotion: Emotions.Confident, reactionCnt: 10, isClicked: false }, | ||
{ emotion: Emotions.Depressed, reactionCnt: 5, isClicked: false }, | ||
{ emotion: Emotions.Disappointed, reactionCnt: 8, isClicked: false }, | ||
{ emotion: Emotions.Embarrassed, reactionCnt: 10, isClicked: false }, | ||
{ emotion: Emotions.Excited, reactionCnt: 5, isClicked: false }, | ||
{ emotion: Emotions.Fun, reactionCnt: 8, isClicked: false }, | ||
{ emotion: Emotions.Grateful, reactionCnt: 10, isClicked: false }, | ||
{ emotion: Emotions.Lonely, reactionCnt: 5, isClicked: false }, | ||
{ emotion: Emotions.Lovely, reactionCnt: 8, isClicked: false }, | ||
{ emotion: Emotions.Not_sure, reactionCnt: 10, isClicked: false }, | ||
{ emotion: Emotions.Sad, reactionCnt: 8, isClicked: false }, | ||
], | ||
onReactionUpdate: (emotion: Emotions, count: number) => { | ||
console.log(`Emotion: ${emotion}, Updated Count: ${count}`); | ||
}, | ||
}, | ||
|
||
play: ({ args }) => { | ||
const logReactionUpdate = (emotion: Emotions, count: number) => { | ||
console.log(`Updated ${emotion}: ${count}`); | ||
}; | ||
|
||
args.onReactionUpdate = logReactionUpdate; | ||
}, | ||
}; | ||
|
||
export const Vertical: Story = { | ||
args: { | ||
isHorizontal: false, | ||
isAddBtnVisible: true, | ||
reactions: [ | ||
{ emotion: Emotions.Angry, reactionCnt: 3, isClicked: false }, | ||
{ emotion: Emotions.Surprised, reactionCnt: 12, isClicked: false }, | ||
], | ||
onReactionUpdate: (emotion: Emotions, count: number) => { | ||
console.log(`Emotion: ${emotion}, Updated Count: ${count}`); | ||
}, | ||
}, | ||
}; |
44 changes: 44 additions & 0 deletions
44
src/entities/ReactionButtonContainer/ui/ReactionButtonContainer.styled.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,44 @@ | ||
import theme from '@/app/styles/theme'; | ||
import styled from 'styled-components'; | ||
|
||
export const StyledReactionContainer = styled.div` | ||
width: 40%; | ||
`; | ||
|
||
export const StyledReactionBtnContainer = styled.div` | ||
display: flex; | ||
flex-wrap: wrap; | ||
justify-content: center; | ||
width: 100%; | ||
& > button { | ||
margin: 5px; | ||
} | ||
`; | ||
|
||
export const StyledEmotionContainer = styled.div` | ||
width: 50%; | ||
position: absolute; | ||
left: 50%; | ||
transform: translateX(-50%); | ||
z-index: 998; | ||
background: white; | ||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); | ||
padding: 10px; | ||
border-radius: 8px; | ||
`; | ||
|
||
export const StyledCloseButton = styled.button` | ||
background-color: transparent; | ||
border: none; | ||
cursor: pointer; | ||
font-size: 16px; | ||
position: absolute; | ||
top: 10px; | ||
right: 10px; | ||
z-index: 999; | ||
&:hover { | ||
color: ${theme.colors.orange_primary}; | ||
} | ||
`; |
131 changes: 131 additions & 0 deletions
131
src/entities/ReactionButtonContainer/ui/ReactionButtonContainer.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,131 @@ | ||
import React, { useEffect, useState } from 'react'; | ||
import { | ||
StyledEmotionContainer, | ||
StyledReactionContainer, | ||
StyledCloseButton, | ||
StyledReactionBtnContainer | ||
} from './ReactionButtonContainer.styled'; | ||
import { Emotions } from '../../../shared/model/EmotionEnum'; | ||
import ReactionButton from '../../../shared/ReactionButton/ui/ReactionButton'; | ||
import ReactionAddButton from '../../../shared/ReactionAddButton/ui/ReactionAddButton'; | ||
import useModal from '@/shared/hooks/useModal'; | ||
import EmotionList from '@/shared/EmotionButtonList/ui/EmotionButtonList'; | ||
import { Reaction } from '../model/reaction'; | ||
|
||
interface ReactionListProps { | ||
reactions: Reaction[]; | ||
isHorizontal: boolean; | ||
isAddBtnVisible: boolean; | ||
onReactionUpdate: ( | ||
emotion: Emotions, | ||
count: number, | ||
isAlreadyClicked: boolean | ||
) => void; | ||
onSelectedEmotionsChange: (selectedEmotions: Emotions[]) => void; | ||
} | ||
|
||
const ReactionButtonContainer: React.FC<ReactionListProps> = ({ | ||
reactions = [], | ||
isHorizontal, | ||
isAddBtnVisible = false, | ||
onReactionUpdate, | ||
onSelectedEmotionsChange | ||
}) => { | ||
const [clickedEmotions, setClickedEmotions] = useState<Emotions[]>([]); | ||
const [updatedReactions, setUpdatedReactions] = | ||
useState<Reaction[]>(reactions); | ||
const [isModalOpen, setIsModalOpen] = useState(false); | ||
|
||
const { openModal, ModalComponent } = useModal(); | ||
|
||
useEffect(() => { | ||
const initialClickedEmotions = reactions | ||
.filter((reaction) => reaction.isClicked) | ||
.map((reaction) => reaction.emotion); | ||
setClickedEmotions(initialClickedEmotions); | ||
}, [reactions]); | ||
|
||
const handleClick = (emotion: Emotions) => { | ||
setClickedEmotions((prev) => { | ||
const isAlreadyClicked = prev.includes(emotion); | ||
const updatedCount = updatedReactions.map((reaction) => { | ||
if (reaction.emotion === emotion) { | ||
const newCount = isAlreadyClicked | ||
? reaction.reactionCnt - 1 | ||
: reaction.reactionCnt + 1; | ||
|
||
onReactionUpdate(emotion, newCount, isAlreadyClicked); | ||
|
||
return { | ||
...reaction, | ||
reactionCnt: newCount | ||
}; | ||
} | ||
return reaction; | ||
}); | ||
|
||
setUpdatedReactions(updatedCount); | ||
|
||
return isAlreadyClicked | ||
? prev.filter((e) => e !== emotion) | ||
: [...prev, emotion]; | ||
}); | ||
}; | ||
|
||
const toggleModal = () => { | ||
setIsModalOpen((prev) => !prev); | ||
}; | ||
|
||
const handleOnClickAddButton = () => { | ||
toggleModal(); | ||
}; | ||
|
||
const onClickTest = (selectedEmotions: Emotions[]) => { | ||
onSelectedEmotionsChange(selectedEmotions); | ||
}; | ||
|
||
const initialSelectedEmotions = reactions | ||
.filter((reaction) => reaction.isClicked) | ||
.map((reaction) => reaction.emotion); | ||
|
||
return ( | ||
<StyledReactionContainer> | ||
<StyledReactionBtnContainer> | ||
{updatedReactions.map(({ emotion, reactionCnt }) => ( | ||
<ReactionButton | ||
key={emotion} | ||
emotion={emotion} | ||
reactionCnt={reactionCnt} | ||
isHorizontal={isHorizontal} | ||
isClicked={clickedEmotions.includes(emotion)} | ||
onClick={handleClick} | ||
/> | ||
))} | ||
|
||
{isAddBtnVisible && ( | ||
<ReactionAddButton | ||
isHorizontal={isHorizontal} | ||
isClicked={false} | ||
onClick={handleOnClickAddButton} | ||
/> | ||
)} | ||
</StyledReactionBtnContainer> | ||
|
||
{isModalOpen && ( | ||
<StyledEmotionContainer> | ||
<StyledCloseButton onClick={toggleModal}> | ||
x | ||
</StyledCloseButton> | ||
<EmotionList | ||
isPrimary={false} | ||
maxSelections={22} | ||
initialSelectedEmotions={initialSelectedEmotions} | ||
onSelectionChange={onClickTest} | ||
/> | ||
</StyledEmotionContainer> | ||
)} | ||
</StyledReactionContainer> | ||
); | ||
}; | ||
|
||
export default ReactionButtonContainer; |
17 changes: 17 additions & 0 deletions
17
src/shared/ReactionAddButton/ui/ReactionAddButton.stories.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,17 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
import ReactionAddButton from './ReactionAddButton'; | ||
|
||
const meta: Meta<typeof ReactionAddButton> = { | ||
component: ReactionAddButton, | ||
title: 'shared/ReactionAddButton', | ||
tags: ['autodocs'], | ||
argTypes: {}, | ||
}; | ||
export default meta; | ||
|
||
type Story = StoryObj<typeof ReactionAddButton>; | ||
|
||
export const Default: Story = { | ||
args: {}, | ||
}; |
31 changes: 31 additions & 0 deletions
31
src/shared/ReactionAddButton/ui/ReactionAddButton.styled.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,31 @@ | ||
import theme from '@/app/styles/theme'; | ||
import styled from 'styled-components'; | ||
|
||
interface reactionAddBtnProps { | ||
isHorizontal: boolean; | ||
clicked: boolean; | ||
} | ||
|
||
export const styledReactionAddBtn = styled.button<reactionAddBtnProps>` | ||
background-color: ${({ clicked }) => | ||
clicked ? theme.colors.orange_selected : theme.colors.white_bg}; | ||
border: 1px solid | ||
${({ clicked }) => | ||
clicked ? theme.colors.orange_primary : theme.colors.gray_normal}; | ||
border-radius: ${({ isHorizontal }) => (isHorizontal ? '30px' : '20px')}; | ||
cursor: pointer; | ||
transition: | ||
background-color 0.2s, | ||
border-color 0.2s; | ||
&:hover { | ||
background-color: ${({ clicked }) => | ||
clicked | ||
? theme.colors.orange_selected | ||
: theme.colors.orange_selected}; | ||
border-color: ${({ clicked }) => | ||
clicked | ||
? theme.colors.orange_primary | ||
: theme.colors.orange_primary}; | ||
} | ||
`; |
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,34 @@ | ||
import { StyledReactionButton } from '../../ReactionButton/ui/ReactionButton.styled'; | ||
import React from 'react'; | ||
import { StyledEmotionIcon } from '../../EmotionIcon/ui/EmotionIcon.styled'; | ||
|
||
interface ReactionAddButtonProps { | ||
isClicked: boolean; | ||
isHorizontal: boolean; | ||
onClick: () => void; | ||
} | ||
|
||
/** 일기 글에 대한 반응을 추가하기 위한 버튼입니다. */ | ||
const ReactionAddButton = ({ | ||
isClicked, | ||
isHorizontal, | ||
onClick | ||
}: ReactionAddButtonProps) => { | ||
const handleClick = () => { | ||
onClick(); | ||
}; | ||
|
||
return ( | ||
<StyledReactionButton | ||
isHorizontal={isHorizontal} | ||
clicked={isClicked} | ||
onClick={handleClick} | ||
> | ||
<StyledEmotionIcon width="80%" height="80%"> | ||
<img src="./reaction_plus.svg" alt="이미지 추가" /> | ||
</StyledEmotionIcon> | ||
</StyledReactionButton> | ||
); | ||
}; | ||
|
||
export default ReactionAddButton; |
Oops, something went wrong.