diff --git a/src/common/component/Mandalart/Mandalart.css.ts b/src/common/component/Mandalart/Mandalart.css.ts new file mode 100644 index 00000000..bfd216d2 --- /dev/null +++ b/src/common/component/Mandalart/Mandalart.css.ts @@ -0,0 +1,17 @@ +import { style } from '@vanilla-extract/css'; + +export const gridDefault = style({ + display: 'grid', + gridTemplateColumns: 'repeat(3, 1fr)', + gap: '1.6rem', + width: 'fit-content', + margin: '0 auto', +}); + +export const gridSmall = style({ + display: 'grid', + gridTemplateColumns: 'repeat(3, 1fr)', + gap: '2rem', + width: 'fit-content', + margin: '0 auto', +}); diff --git a/src/common/component/Mandalart/Mandalart.stories.tsx b/src/common/component/Mandalart/Mandalart.stories.tsx new file mode 100644 index 00000000..fae6f167 --- /dev/null +++ b/src/common/component/Mandalart/Mandalart.stories.tsx @@ -0,0 +1,107 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; + +import Mandalart, { type Cycle } from './Mandalart'; +import { MOCK_MANDALART_DATA } from './mock'; + +const meta = { + title: 'Components/Mandalart', + component: Mandalart, + parameters: { + layout: 'centered', + backgrounds: { + default: 'dark', + }, + }, + tags: ['autodocs'], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const CUSTOM_GOALS = { + mainGoal: '나인도트 1등하기', + subGoals: [ + { + title: '이현준 갈구기', + position: 0, + cycle: 'DAILY' as Cycle, + }, + { + title: '매일 운동하기', + position: 1, + cycle: 'DAILY' as Cycle, + }, + { + title: '일찍 일어나기', + position: 2, + cycle: 'DAILY' as Cycle, + }, + { + title: '계획 세우기', + position: 3, + cycle: 'WEEKLY' as Cycle, + }, + { + title: '시간 관리하기', + position: 4, + cycle: 'WEEKLY' as Cycle, + }, + { + title: '건강 관리하기', + position: 5, + cycle: 'DAILY' as Cycle, + }, + { + title: '긍정적으로 생각하기', + position: 6, + cycle: 'DAILY' as Cycle, + }, + { + title: '꾸준히 노력하기', + position: 7, + cycle: 'DAILY' as Cycle, + }, + ], + completedGoals: [1, 3, 5], +}; + +export const Default: Story = { + args: { + mainGoal: '메인 목표를 입력하세요', + subGoals: MOCK_MANDALART_DATA.subGoals, + }, + render: (args) => ( +
+
+

Default 사이즈

+ +
+
+

Small 사이즈

+ +
+
+ ), +}; + +export const Small: Story = { + args: { + mainGoal: '메인 목표를 입력하세요', + subGoals: CUSTOM_GOALS.subGoals, + size: 'small', + }, + render: (args) => , +}; + +export const WithCustomGoals: Story = { + args: CUSTOM_GOALS, + render: (args) => , +}; + +export const WithCustomGoalsSmall: Story = { + args: { + ...CUSTOM_GOALS, + size: 'small', + }, + render: (args) => , +}; diff --git a/src/common/component/Mandalart/Mandalart.tsx b/src/common/component/Mandalart/Mandalart.tsx new file mode 100644 index 00000000..c9978e05 --- /dev/null +++ b/src/common/component/Mandalart/Mandalart.tsx @@ -0,0 +1,61 @@ +import { useState } from 'react'; + +import { Square } from './Square'; +import * as styles from './Mandalart.css'; +import { MOCK_MANDALART_DATA } from './mock'; + +export type Cycle = 'DAILY' | 'WEEKLY' | 'ONCE'; +export type MandalartSize = 'small' | 'default'; + +export interface SubGoal { + title: string; + position: number; + cycle: Cycle; +} + +interface MandalartProps { + mainGoal?: string; + subGoals?: SubGoal[]; + size?: MandalartSize; +} + +const CENTER_INDEX = 4; + +const Mandalart = ({ + mainGoal = MOCK_MANDALART_DATA.mainGoal, + subGoals = MOCK_MANDALART_DATA.subGoals, + size = 'default', +}: MandalartProps) => { + const [selectedGoal, setSelectedGoal] = useState(null); + + const handleGoalClick = (position: number) => { + setSelectedGoal(selectedGoal === position ? null : position); + }; + + const renderSquare = (index: number) => { + if (index === CENTER_INDEX) { + return ; + } + + const subGoalIndex = index > CENTER_INDEX ? index - 1 : index; + const subGoal = subGoals[subGoalIndex]; + + return ( + handleGoalClick(subGoalIndex)} + size={size} + /> + ); + }; + + const squares = Array(9) + .fill(null) + .map((_, index) => renderSquare(index)); + + return
{squares}
; +}; + +export default Mandalart; diff --git a/src/common/component/Mandalart/Square/Square.css.ts b/src/common/component/Mandalart/Square/Square.css.ts new file mode 100644 index 00000000..dfed555f --- /dev/null +++ b/src/common/component/Mandalart/Square/Square.css.ts @@ -0,0 +1,87 @@ +import { style } from '@vanilla-extract/css'; + +import { colors, fonts } from '@/style/token'; + +export const squareContainer = style({ + display: 'grid', + margin: '0 auto', +}); + +const baseCellDefault = style({ + borderRadius: '8px', + cursor: 'pointer', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + textAlign: 'center', + width: '19.6rem', + height: '19.6rem', + boxSizing: 'border-box', +}); + +const baseCellSmall = style({ + borderRadius: '8px', + cursor: 'pointer', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + textAlign: 'center', + width: '16rem', + height: '16rem', + boxSizing: 'border-box', +}); + +export const mainCellDefault = style([ + baseCellDefault, + fonts.title03, + { + color: colors.white01, + backgroundImage: colors.gradient04, + }, +]); + +export const mainCellSmall = style([ + baseCellSmall, + fonts.subtitle01, + { + color: colors.white01, + backgroundImage: colors.gradient04, + padding: '1.4rem', + }, +]); + +export const subCellDefault = style([ + baseCellDefault, + fonts.subtitle01, + { + color: colors.grey8, + background: colors.grey2, + ':hover': { + background: colors.grey3, + }, + selectors: { + '&[data-completed="true"]': { + border: '0.4rem solid #305088', + background: colors.grey2, + }, + }, + }, +]); + +export const subCellSmall = style([ + baseCellSmall, + fonts.subtitle05, + { + color: colors.grey8, + background: colors.grey2, + ':hover': { + background: colors.grey3, + }, + selectors: { + '&[data-completed="true"]': { + border: '0.3rem solid #305088', + background: colors.grey2, + }, + }, + }, +]); diff --git a/src/common/component/Mandalart/Square/Square.stories.tsx b/src/common/component/Mandalart/Square/Square.stories.tsx new file mode 100644 index 00000000..6bb5411e --- /dev/null +++ b/src/common/component/Mandalart/Square/Square.stories.tsx @@ -0,0 +1,116 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; + +import { Square } from '.'; + +import { colors } from '@/style/token'; + +const meta = { + title: 'Components/Square', + component: Square.Main, + parameters: { + layout: 'centered', + backgrounds: { + default: 'dark', + }, + }, + tags: ['autodocs'], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const handleClick = () => {}; + +export const Default: Story = { + args: { + content: '상위 목표', + }, + render: (args) => ( +
+
+

Default 사이즈

+

+ 메인: title03 / 서브: subtitle01 +

+
+ + +
+
+
+

Small 사이즈

+

+ 메인: body04 / 서브: subtitle05 +

+
+ + +
+
+
+ ), +}; + +export const MainGoal: Story = { + args: { + content: '메인 목표를 입력하세요', + }, + render: (args) => ( +
+
+

Default 사이즈 (title03)

+ +
+
+

Small 사이즈 (body04)

+ +
+
+ ), +}; + +export const SubGoalStates: Story = { + args: { + content: '세부 목표를 입력하세요', + }, + render: (args) => ( +
+
+

Default 사이즈 (subtitle01)

+
+
+

기본 상태

+ +
+
+

완료 상태

+ +
+
+
+
+

Small 사이즈 (subtitle05)

+
+
+

기본 상태

+ +
+
+

완료 상태

+ +
+
+
+
+ ), +}; diff --git a/src/common/component/Mandalart/Square/Square.tsx b/src/common/component/Mandalart/Square/Square.tsx new file mode 100644 index 00000000..6034773e --- /dev/null +++ b/src/common/component/Mandalart/Square/Square.tsx @@ -0,0 +1,49 @@ +import * as styles from './Square.css'; +import type { MandalartSize } from '../Mandalart'; + +export interface SquareProps { + children: React.ReactNode; +} + +export interface CellProps { + content: string; + size?: MandalartSize; +} + +export interface SubCellProps extends CellProps { + isCompleted: boolean; + onClick: () => void; +} + +export const Root = ({ children }: SquareProps) => { + return
{children}
; +}; + +export const Main = ({ content, size = 'default' }: CellProps) => { + return ( +
+
+ {content} +
+
+ ); +}; + +export const Sub = ({ content, isCompleted, onClick, size = 'default' }: SubCellProps) => { + return ( +
+
+ {content} +
+
+ ); +}; + +export const Square = { + Main, + Sub, +}; diff --git a/src/shared/component/Mandalart/Square/index.ts b/src/common/component/Mandalart/Square/index.ts similarity index 100% rename from src/shared/component/Mandalart/Square/index.ts rename to src/common/component/Mandalart/Square/index.ts diff --git a/src/shared/component/Mandalart/mock.ts b/src/common/component/Mandalart/mock.ts similarity index 77% rename from src/shared/component/Mandalart/mock.ts rename to src/common/component/Mandalart/mock.ts index a43bf5e6..4819abfb 100644 --- a/src/shared/component/Mandalart/mock.ts +++ b/src/common/component/Mandalart/mock.ts @@ -1,10 +1,7 @@ -import type { MainGoal, SubGoal } from './Mandalart'; +import type { SubGoal } from './Mandalart'; -export const MOCK_MANDALART_DATA: { mainGoal: MainGoal; subGoals: SubGoal[] } = { - mainGoal: { - title: '나인도트 1등하기', - position: 4, - }, +export const MOCK_MANDALART_DATA = { + mainGoal: '나인도트 1등하기', subGoals: [ { title: '이현준 갈구기', @@ -46,5 +43,6 @@ export const MOCK_MANDALART_DATA: { mainGoal: MainGoal; subGoals: SubGoal[] } = position: 7, cycle: 'DAILY', }, - ], + ] as SubGoal[], + completedGoals: [], }; diff --git a/src/shared/component/Mandalart/Mandalart.css.ts b/src/shared/component/Mandalart/Mandalart.css.ts deleted file mode 100644 index 20a2cb00..00000000 --- a/src/shared/component/Mandalart/Mandalart.css.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { style } from '@vanilla-extract/css'; - -export const grid = style({ - display: 'grid', - gridTemplateColumns: 'repeat(3, 1fr)', - gap: '1.6rem', - width: 'fit-content', - margin: '0 auto', -}); diff --git a/src/shared/component/Mandalart/Mandalart.stories.tsx b/src/shared/component/Mandalart/Mandalart.stories.tsx deleted file mode 100644 index 0cb0d0e2..00000000 --- a/src/shared/component/Mandalart/Mandalart.stories.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react-vite'; -import Mandalart from './Mandalart'; - -const meta = { - title: 'Components/Mandalart', - component: Mandalart, - parameters: { - layout: 'centered', - backgrounds: { - default: 'dark', - }, - }, - tags: ['autodocs'], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: {}, -}; - -export const WithCustomGoals: Story = { - args: { - mainGoal: { - title: '나인도트 1등하기', - position: 4, - }, - subGoals: [ - { - title: '이현준 갈구기', - position: 0, - cycle: 'DAILY', - }, - { - title: '매일 운동하기', - position: 1, - cycle: 'DAILY', - }, - { - title: '일찍 일어나기', - position: 2, - cycle: 'DAILY', - }, - { - title: '계획 세우기', - position: 3, - cycle: 'WEEKLY', - }, - { - title: '시간 관리하기', - position: 4, - cycle: 'WEEKLY', - }, - { - title: '건강 관리하기', - position: 5, - cycle: 'DAILY', - }, - { - title: '긍정적으로 생각하기', - position: 6, - cycle: 'DAILY', - }, - { - title: '꾸준히 노력하기', - position: 7, - cycle: 'DAILY', - }, - ], - }, -}; diff --git a/src/shared/component/Mandalart/Mandalart.tsx b/src/shared/component/Mandalart/Mandalart.tsx deleted file mode 100644 index 4fc0f638..00000000 --- a/src/shared/component/Mandalart/Mandalart.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { useState } from 'react'; - -import { Square } from './Square'; -import * as styles from './Mandalart.css'; -import { MOCK_MANDALART_DATA } from './mock'; - -export type Cycle = 'DAILY' | 'WEEKLY' | 'ONCE'; - -export interface SubGoal { - title: string; - position: number; - cycle: Cycle; -} - -export interface MainGoal { - title: string; - position: number; -} - -interface MandalartProps { - mainGoal?: MainGoal; - subGoals?: SubGoal[]; - onSubGoalSelect?: (position: number) => void; -} - -const DEFAULT_MAIN_GOAL: MainGoal = MOCK_MANDALART_DATA.mainGoal; -const DEFAULT_SUB_GOALS: SubGoal[] = MOCK_MANDALART_DATA.subGoals; - -const CENTER_INDEX = 4; - -const Mandalart = ({ - mainGoal = DEFAULT_MAIN_GOAL, - subGoals = DEFAULT_SUB_GOALS, - onSubGoalSelect, -}: MandalartProps) => { - const [selectedIndex, setSelectedIndex] = useState(null); - - const handleClick = (index: number) => { - if (index === CENTER_INDEX) { - return; - } - const newSelectedIndex = selectedIndex === index ? null : index; - setSelectedIndex(newSelectedIndex); - - if (newSelectedIndex !== null) { - const subGoalIndex = - newSelectedIndex > CENTER_INDEX ? newSelectedIndex - 1 : newSelectedIndex; - onSubGoalSelect?.(subGoalIndex); - } - }; - - const renderSquare = (index: number) => { - if (index === CENTER_INDEX) { - return ; - } - - const subGoalIndex = index > CENTER_INDEX ? index - 1 : index; - const subGoal = subGoals[subGoalIndex]; - - return ( - handleClick(index)} - /> - ); - }; - - const squares = Array(9) - .fill(null) - .map((_, index) => renderSquare(index)); - - return
{squares}
; -}; - -export default Mandalart; diff --git a/src/shared/component/Mandalart/Square/Square.css.ts b/src/shared/component/Mandalart/Square/Square.css.ts deleted file mode 100644 index 4d0abb72..00000000 --- a/src/shared/component/Mandalart/Square/Square.css.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { style } from '@vanilla-extract/css'; - -import { colors, fonts } from '@/style/token'; - -export const squareContainer = style({ - display: 'grid', - gap: '1rem', - padding: '1rem', - margin: '0 auto', -}); - -const baseCell = style({ - color: colors.white01, - borderRadius: '8px', - cursor: 'pointer', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - textAlign: 'center', - width: '19.6rem', - height: '19.6rem', - boxSizing: 'border-box', -}); - -export const mainCell = style([ - baseCell, - fonts.title03, - { - backgroundImage: colors.gradient04, - }, -]); - -export const subCell = style([ - baseCell, - fonts.subtitle01, - { - background: colors.grey2, - ':hover': { - background: colors.grey3, - }, - selectors: { - '&[data-completed="true"]': { - border: '0.4rem solid #305088', - background: colors.grey2, - }, - }, - }, -]); diff --git a/src/shared/component/Mandalart/Square/Square.stories.tsx b/src/shared/component/Mandalart/Square/Square.stories.tsx deleted file mode 100644 index 51f05edc..00000000 --- a/src/shared/component/Mandalart/Square/Square.stories.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react-vite'; - -import { Square } from '.'; -import { colors } from '@/style/token'; - -const meta = { - title: 'Components/Square', - component: Square.Root, - parameters: { - layout: 'centered', - backgrounds: { - default: 'dark', - }, - }, - tags: ['autodocs'], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - children: ( - <> - - - - ), - }, -}; - -export const MainGoal: Story = { - args: { - children: , - }, -}; - -export const SubGoalStates: Story = { - args: { - children: ( -
-
-

기본 상태

- -
-
-

완료 상태

- -
-
- ), - }, -}; diff --git a/src/shared/component/Mandalart/Square/Square.tsx b/src/shared/component/Mandalart/Square/Square.tsx deleted file mode 100644 index b101d37f..00000000 --- a/src/shared/component/Mandalart/Square/Square.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import * as styles from './Square.css.ts'; - -export interface SquareProps { - children: React.ReactNode; -} - -export interface CellProps { - content?: string; - onClick?: () => void; - isCompleted?: boolean; -} - -export const Root = ({ children }: SquareProps) => { - return
{children}
; -}; - -export const Main = ({ content }: CellProps) => { - return
{content}
; -}; - -export const Sub = ({ content, onClick, isCompleted }: CellProps) => { - return ( -
- {content} -
- ); -}; diff --git a/src/style/token/color.css.ts b/src/style/token/color.css.ts index 2d79b89a..2c0cad7f 100644 --- a/src/style/token/color.css.ts +++ b/src/style/token/color.css.ts @@ -11,7 +11,7 @@ export const colors = { grey5: '#41454C', grey6: '#5A5E66', grey7: '#999DA4', - grey8: '#B1B6BE)', + grey8: '#B1B6BE', grey9: '#C5C8CE', grey10: '#E3E4E5', grey11: '#FDFDFD',