diff --git a/package.json b/package.json index 8c1eeddb..61c3cd24 100644 --- a/package.json +++ b/package.json @@ -1,61 +1,61 @@ { - "name": "client", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc -b && vite build", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview", - "stylelint": "stylelint './src/**/*.{ts,tsx}' --fix", - "svgr": "npx @svgr/cli -d src/assets/svg --ignore-existing --typescript --no-dimensions public/svg", - "storybook": "storybook dev -p 6006", - "build-storybook": "storybook build" - }, - "dependencies": { - "@emotion/react": "^11.11.4", - "@emotion/styled": "^11.11.5", - "@svgr/cli": "^8.1.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-router-dom": "^6.24.1", - "vite-plugin-svgr": "^4.2.0" - }, - "devDependencies": { - "@chromatic-com/storybook": "^1.6.1", - "@storybook/addon-essentials": "^8.1.11", - "@storybook/addon-interactions": "^8.1.11", - "@storybook/addon-links": "^8.1.11", - "@storybook/addon-onboarding": "^8.1.11", - "@storybook/blocks": "^8.1.11", - "@storybook/react": "^8.1.11", - "@storybook/react-vite": "^8.1.11", - "@storybook/test": "^8.1.11", - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", - "@typescript-eslint/eslint-plugin": "^7.13.1", - "@typescript-eslint/parser": "^7.13.1", - "@vitejs/plugin-react": "^4.3.1", - "eslint": "^8.57.0", - "eslint-config-airbnb": "^19.0.4", - "eslint-config-airbnb-typescript": "^18.0.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-import": "^2.25.3", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-react": "^7.28.0", - "eslint-plugin-react-hooks": "^4.6.2", - "eslint-plugin-react-refresh": "^0.4.7", - "eslint-plugin-storybook": "^0.8.0", - "postcss": "^8.4.21", - "postcss-styled-syntax": "^0.6.4", - "prettier": "^3.3.2", - "storybook": "^8.1.11", - "stylelint": "^16.6.1", - "stylelint-config-standard": "^36.0.1", - "stylelint-order": "^6.0.4", - "typescript": "^5.2.2", - "vite": "^5.3.1" - } + "name": "client", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview", + "stylelint": "stylelint './src/**/*.{ts,tsx}' --fix", + "svgr": "npx @svgr/cli -d src/assets/svg --ignore-existing --typescript --no-dimensions public/svg", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build" + }, + "dependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5", + "@svgr/cli": "^8.1.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.24.1", + "vite-plugin-svgr": "^4.2.0" + }, + "devDependencies": { + "@chromatic-com/storybook": "^1.6.1", + "@storybook/addon-essentials": "^8.1.11", + "@storybook/addon-interactions": "^8.1.11", + "@storybook/addon-links": "^8.1.11", + "@storybook/addon-onboarding": "^8.1.11", + "@storybook/blocks": "^8.1.11", + "@storybook/react": "^8.1.11", + "@storybook/react-vite": "^8.1.11", + "@storybook/test": "^8.1.11", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@typescript-eslint/eslint-plugin": "^7.13.1", + "@typescript-eslint/parser": "^7.13.1", + "@vitejs/plugin-react": "^4.3.1", + "eslint": "^8.57.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-airbnb-typescript": "^18.0.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-react": "^7.28.0", + "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-refresh": "^0.4.7", + "eslint-plugin-storybook": "^0.8.0", + "postcss": "^8.4.21", + "postcss-styled-syntax": "^0.6.4", + "prettier": "^3.3.2", + "storybook": "^8.1.11", + "stylelint": "^16.6.1", + "stylelint-config-standard": "^36.0.1", + "stylelint-order": "^6.0.4", + "typescript": "^5.2.2", + "vite": "^5.3.1" + } } diff --git a/src/assets/svg/calendar-minus-01.svg b/src/assets/svg/calendar-minus-01.svg new file mode 100644 index 00000000..83d2b236 --- /dev/null +++ b/src/assets/svg/calendar-minus-01.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/svg/clock.svg b/src/assets/svg/clock.svg new file mode 100644 index 00000000..58dfc083 --- /dev/null +++ b/src/assets/svg/clock.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/svg/index.ts b/src/assets/svg/index.ts index 1e23873a..85e6a7e4 100644 --- a/src/assets/svg/index.ts +++ b/src/assets/svg/index.ts @@ -1,13 +1,21 @@ import Icn_arrow_narrow_right from '@/assets/svg/arrow-narrow-right.svg?react'; +import Icn_calander from '@/assets/svg/calendar-minus-01.svg?react'; +import Icn_date_clock from '@/assets/svg/clock.svg?react'; import Icn_clock from '@/assets/svg/icn_clock.svg?react'; import Icn_nav_calendar from '@/assets/svg/icn_nav_calendar.svg?react'; import Icn_nav_dashboard from '@/assets/svg/icn_nav_dashboard.svg?react'; import Icn_nav_setting from '@/assets/svg/icn_nav_setting.svg?react'; import Icn_nav_today from '@/assets/svg/icn_nav_today.svg?react'; +import Icn_line from '@/assets/svg/line164.svg?react'; +import IcnXCricle from '@/assets/svg/x-circle.svg?react'; const Icons = { Icn_clock, Icn_arrow_narrow_right, + Icn_calander, + Icn_date_clock, + Icn_line, + IcnXCricle, Navbar: { Icn_nav_calendar, Icn_nav_dashboard, diff --git a/src/assets/svg/line164.svg b/src/assets/svg/line164.svg new file mode 100644 index 00000000..6dafb3d3 --- /dev/null +++ b/src/assets/svg/line164.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svg/x-circle.svg b/src/assets/svg/x-circle.svg new file mode 100644 index 00000000..417c9860 --- /dev/null +++ b/src/assets/svg/x-circle.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/components/common/BtnDate/BtnDate.tsx b/src/components/common/BtnDate/BtnDate.tsx new file mode 100644 index 00000000..e6fba4ce --- /dev/null +++ b/src/components/common/BtnDate/BtnDate.tsx @@ -0,0 +1,145 @@ +import { css } from '@emotion/react'; +import styled from '@emotion/styled'; +import { useState } from 'react'; + +import BtnDateText, { TextWrapper } from './BtnDateText'; + +import Icons from '@/assets/svg/index'; + +interface BtnDateProps { + date?: string; + time?: string; + size?: string; +} + +function BtnDate(props: BtnDateProps) { + const { date = '마감 기한', time = '마감 시간', size = 'big' } = props; + const [isPressed, setIsPressed] = useState(false); + const [isClicked, setIsClicked] = useState(false); + + const handleMouseDown = () => { + setIsPressed(true); + }; + + const handleMouseUp = () => { + setIsPressed(false); + setIsClicked((prev) => !prev); + }; + + const isDefaultDate = date === '마감 기한'; + const isDefaultTime = time === '마감 시간'; + + return ( + + } text={date} isDefault={isDefaultDate} size={size} /> + + } text={time} isDefault={isDefaultTime} size={size} /> + + + ); +} + +export default BtnDate; + +const XIcon = styled((props: React.SVGProps & { isClicked: boolean }) => { + const { isClicked, ...rest } = props; + return ; +})<{ isClicked: boolean }>` + display: ${({ isClicked }) => (isClicked ? 'flex' : 'none')}; + width: 2rem; + height: 2rem; +`; + +const CalanderIcon = styled(Icons.Icn_calander, { target: 'CalanderIcon' })` + display: flex; + align-items: center; + justify-content: center; + width: 1.4rem; + height: 1.4rem; +`; + +const ClockIcon = styled(Icons.Icn_date_clock, { target: 'ClockIcon' })` + display: flex; + align-items: center; + justify-content: center; + width: 1.4rem; + height: 1.4rem; +`; + +const LineIcon = styled(Icons.Icn_line, { target: 'LineIcon' })<{ size: string }>` + display: flex; + align-items: center; + justify-content: center; + width: 0.1rem; + height: ${({ size }) => (size === 'big' ? '2.2rem' : '1.2rem')}; +`; + +const BtnDateLayout = styled.div<{ + isPressed: boolean; + isClicked: boolean; + size: string; + isDefaultDate: boolean; + isDefaultTime: boolean; +}>` + display: flex; + gap: 1rem; + align-items: center; + width: fit-content; + min-width: 1.8rem; + padding: 0.5rem 1rem; + + background: ${({ theme }) => theme.palette.white}; + cursor: pointer; + border: 1px solid ${({ theme }) => theme.palette.grey3}; + border-color: ${({ isClicked, theme }) => (isClicked ? theme.palette.primary : theme.palette.grey3)}; + border-radius: 8px; + + ${({ isClicked, size, theme }) => + isClicked && + css` + padding-right: ${size === 'big' ? '0.6rem' : '0.2rem'}; + + border-color: ${theme.palette.primary}; + border-width: 2px; + `} + + ${({ isPressed, theme }) => + isPressed && + css` + border-width: 0; + + ${TextWrapper} { + color: ${theme.palette.white}; + } + `} + + &:hover { + color: ${({ isPressed, theme }) => (isPressed ? theme.palette.white : theme.palette.grey6)}; + + background: ${({ isPressed, theme }) => (isPressed ? theme.palette.grey5 : theme.palette.grey4)}; + border-width: 0; + + ${TextWrapper} { + color: ${({ isDefaultDate, isDefaultTime, theme }) => + isDefaultDate || isDefaultTime ? theme.palette.grey6 : theme.palette.black}; + } + } + + &:hover ${CalanderIcon}, &:hover ${ClockIcon}, &:hover ${LineIcon} { + path { + stroke: ${({ isPressed, theme }) => (isPressed ? theme.palette.grey4 : theme.palette.grey5)}; + } + + line { + stroke: ${({ isPressed, theme }) => (isPressed ? theme.palette.grey4 : theme.palette.grey5)}; + } + } +`; diff --git a/src/components/common/BtnDate/BtnDateText.tsx b/src/components/common/BtnDate/BtnDateText.tsx new file mode 100644 index 00000000..fb2c215d --- /dev/null +++ b/src/components/common/BtnDate/BtnDateText.tsx @@ -0,0 +1,38 @@ +import styled from '@emotion/styled'; + +interface BtnDateTextProps { + icon: React.ReactNode; + text: string; + isDefault: boolean; + size: string; +} + +function BtnDateText(props: BtnDateTextProps) { + const { icon, text, isDefault, size } = props; + return ( + + {icon} + + {text} + + + ); +} + +export default BtnDateText; + +export const TextWrapper = styled('div', { target: 'TextWrapper' })<{ isDefault: boolean; size: string }>` + display: flex; + flex-direction: column; + gap: 1rem; + align-items: flex-start; + + color: ${({ isDefault, theme }) => (isDefault ? theme.palette.grey5 : theme.palette.black)}; + font-size: ${({ size }) => (size === 'big' ? '1.4rem' : '1.2rem')}; +`; + +const BtnDateContainer = styled.div` + display: flex; + gap: 0.4rem; + align-items: center; +`; diff --git a/src/components/common/BtnDate/BtnStagingDate.tsx b/src/components/common/BtnDate/BtnStagingDate.tsx new file mode 100644 index 00000000..38f8fbdd --- /dev/null +++ b/src/components/common/BtnDate/BtnStagingDate.tsx @@ -0,0 +1,47 @@ +import styled from '@emotion/styled'; + +function BtnStagingDate() { + return ( + + ⇥ 마감 기한 설정 + + ); +} + +export default BtnStagingDate; + +const BtnStagingDateText = styled('p', { target: 'BtnStagingDateText' })` + color: ${({ theme }) => theme.palette.grey5}; + font-size: 1.2rem; + text-align: center; +`; + +const BtnStagingDateLayout = styled('div', { target: 'BtnStagingDateLayout' })` + display: flex; + gap: 0.8rem; + align-items: center; + justify-content: center; + width: fit-content; + height: 2.2rem; + padding: 0.3rem 1.2rem; + + background: ${({ theme }) => theme.palette.grey3}; + cursor: pointer; + border-radius: 8px; + + &:hover { + background: ${({ theme }) => theme.palette.grey4}; + + ${BtnStagingDateText} { + color: ${({ theme }) => theme.palette.grey6}; + } + } + + &:active { + background: ${({ theme }) => theme.palette.grey5}; + + ${BtnStagingDateText} { + color: ${({ theme }) => theme.palette.white}; + } + } +`; diff --git a/src/pages/Today.tsx b/src/pages/Today.tsx index c72b5f25..101c7bad 100644 --- a/src/pages/Today.tsx +++ b/src/pages/Today.tsx @@ -1,3 +1,5 @@ +import BtnDate from '@/components/common/BtnDate/BtnDate'; +import BtnStagingDate from '@/components/common/BtnDate/BtnStagingDate'; import NavBar from '@/components/common/NavBar'; import TextboxDailydate from '@/components/common/textbox/TextboxDailydate'; import TextboxInput from '@/components/common/textbox/TextboxInput'; @@ -23,6 +25,8 @@ function Today() { + + ); } diff --git a/src/styles/theme.ts b/src/styles/theme.ts index c6dbc4d7..f1aa3199 100644 --- a/src/styles/theme.ts +++ b/src/styles/theme.ts @@ -29,6 +29,15 @@ const palette = { GREY_04: '#626273', GREY_05: '#464656', GREY_06: '#34343C', + + // 지원 임시색상 + grey3: '#E9E9EE', + grey4: '#C6C6D0', + grey5: '#9090A0', + grey6: '#626273', + black: '#212121', + primary: '#3876F6', + white: '#FFFFFF', }; const fonts = {