diff --git a/src/assets/icons/icon_copy.svg b/src/assets/icons/icon_copy.svg new file mode 100644 index 0000000..f61f802 --- /dev/null +++ b/src/assets/icons/icon_copy.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/pages/CreateRollingPaperPage/components/ColorPickerModal.jsx b/src/pages/CreateRollingPaperPage/components/ColorPickerModal.jsx index ca6cbcb..d9ac881 100644 --- a/src/pages/CreateRollingPaperPage/components/ColorPickerModal.jsx +++ b/src/pages/CreateRollingPaperPage/components/ColorPickerModal.jsx @@ -2,25 +2,62 @@ import { useState } from 'react'; import { HexColorPicker } from 'react-colorful'; import styles from './ColorPickerModal.module.scss'; import Modal from '../../../components/Modal'; +import copyIcon from '@/assets/icons/icon_copy.svg'; +import { useToast } from '@/hooks/useToast'; const ColorPickerModal = ({ initialColor = '#ffffff', onSubmit, onClose }) => { const [color, setColor] = useState(initialColor); + const { showToast } = useToast(); + + const handleColorInputChange = (e) => { + setColor(e.target.value); + }; + + const handleColorCopyClick = () => { + const isHexColor = (str) => /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(str); + if (isHexColor(color)) { + showToast({ type: 'success', message: '복사 성공', timer: 1000 }); + } else { + showToast({ type: 'fail', message: '유효한 값이 아닙니다', timer: 1000 }); + } + }; return ( - -
+ +
HEX -
-
-
{color}
+
+
+ +
+ +
- - + + ); diff --git a/src/pages/CreateRollingPaperPage/components/ColorPickerModal.module.scss b/src/pages/CreateRollingPaperPage/components/ColorPickerModal.module.scss index ae60ec0..65d56d4 100644 --- a/src/pages/CreateRollingPaperPage/components/ColorPickerModal.module.scss +++ b/src/pages/CreateRollingPaperPage/components/ColorPickerModal.module.scss @@ -1,39 +1,115 @@ .modal-styler { - padding: 10px; + padding: 20px; } .button-area { display: flex; flex-direction: row; justify-content: center; + gap: 10px; + margin-top: 15px; } -.color-info-box { - display: flex; - flex-direction: column; - align-items: center; - padding-top: 10px; - gap: 5px; -} +.color-picker-modal { + &__description-container { + display: flex; + flex-direction: column; + align-items: center; + padding-top: 10px; + gap: 5px; + } -.color-info { - border: 1px solid var(--color-gray-200); - height: 40px; - width: 100%; - border-radius: 5px; - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; -} + &__description { + border: 1px solid var(--color-gray-200); + height: 40px; + width: 100%; + border-radius: 5px; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: 5px; + } -.color { - aspect-ratio: 1; - height: 100%; - border-radius: 5px; -} + &__color-display { + aspect-ratio: 1; + height: 100%; + border-radius: 5px; + } + + &__color-value-input { + text-align: center; + border: none; + font-size: 18px; + border-left: 1px solid var(--color-gray-200); + width: 8ch; + text-align: center; + flex-grow: 1; + } + + &__copy-button-area { + aspect-ratio: 1; + height: 100%; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + } + + &__copy-button { + aspect-ratio: 1; + width: 65%; + border-radius: 20%; + cursor: pointer; + &:hover { + background-color: var(--color-gray-100); + } + } + + &__button-submit { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 10px; + width: 100%; + background-color: var(--color-purple-600); + border: none; + border-radius: 8px; + font-weight: 400; + font-size: 13px; + line-height: 25px; + color: var(--color-white); + cursor: pointer; + + &:hover { + background-color: var(--color-purple-700); + } + + &:disabled { + background-color: var(--color-gray-300); + cursor: auto; + } + } + + &__button-cancel { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 10px; + width: 50%; + background-color: var(--color-white); + border: 1px solid var(--color-purple-600); + border-radius: 8px; + font-weight: 400; + font-size: 13px; + line-height: 25px; + color: var(--color-purple-600); + cursor: pointer; -.color-context { - flex-grow: 1; - text-align: center; + &:hover { + background-color: var(--color-purple-100); + } + } } \ No newline at end of file diff --git a/src/pages/CreateRollingPaperPage/components/ColorSwatch.jsx b/src/pages/CreateRollingPaperPage/components/ColorSwatch.jsx index 9934ac8..23b5a7f 100644 --- a/src/pages/CreateRollingPaperPage/components/ColorSwatch.jsx +++ b/src/pages/CreateRollingPaperPage/components/ColorSwatch.jsx @@ -1,14 +1,14 @@ import styles from './ColorSwatch.module.scss'; import iconSelectedDark from '@/assets/icons/icon_success_darkgray_lg.svg'; import iconSelectedLight from '@/assets/icons/icon_success_gray_lg.svg'; -import { isColorDark } from '../../../utils/getIsColorDark'; +import { getIsColorDark } from '../../../utils/getIsColorDark'; const ColorSwatch = ({ hexColor, selectedColor, onColorSelect }) => { return (
onColorSelect(hexColor)}>
{selectedColor === hexColor && - (isColorDark(hexColor) ? ( + (getIsColorDark(hexColor) ? ( ) : ( diff --git a/src/utils/getBackgroundStylesFromPostData.js b/src/utils/getBackgroundStylesFromPostData.js new file mode 100644 index 0000000..44b0094 --- /dev/null +++ b/src/utils/getBackgroundStylesFromPostData.js @@ -0,0 +1,29 @@ +import { getColorFromCloudinaryImageUrl } from './getColorFromCloudinaryImageUrl'; + +export const getBackgroundStylesFromPostData = ({ backgroundColor, backgroundImageURL }) => { + let backgroundStyle = { + backgroundColor: 'none', + backgroundImage: 'none', + backgroundRepeat: 'no-repeaet', + backgroundPosition: 'center', + backgroundSize: 'cover', + }; + + if (!backgroundImageURL) { + backgroundStyle.backgroundColor = `var(--color-${backgroundColor}-200)`; + } else { + const urlObj = new URL(backgroundImageURL); + const parts = urlObj.pathname.split('/'); // ['','dxho7f5dm','image','upload','v1749409044','colors','6a6a6a.png'] + const folderCandidate = parts[5]; // public_id 시작 부분 (index 5) + if (folderCandidate === 'colors') { + const imageHexColor = getColorFromCloudinaryImageUrl(backgroundImageURL); + backgroundStyle.backgroundColor = imageHexColor; + } else if (folderCandidate === 'images') { + backgroundStyle.backgroundImage = `url(${backgroundImageURL})`; + } else { + backgroundStyle.backgroundImage = `url(${backgroundImageURL})`; + } + } + + return backgroundStyle; +}; diff --git a/src/utils/getColorFromCloudinaryImage.js b/src/utils/getColorFromCloudinaryImageUrl.js similarity index 91% rename from src/utils/getColorFromCloudinaryImage.js rename to src/utils/getColorFromCloudinaryImageUrl.js index 5144b2f..4add803 100644 --- a/src/utils/getColorFromCloudinaryImage.js +++ b/src/utils/getColorFromCloudinaryImageUrl.js @@ -1,6 +1,6 @@ //cloudinary 파일명에서 색상을 읽어옵니다. //ex. ...colors/131313.svg는 #131313 입니다. -export const getColorFromCloudinaryImage = (url) => { +export const getColorFromCloudinaryImageUrl = (url) => { if (!url) return null; const urlObj = new URL(url); const parts = urlObj.pathname.split('/'); // ['','dxho7f5dm','image','upload','v1749409044','colors','6a6a6a.png'] diff --git a/src/utils/getContentStylesFromPostData.js b/src/utils/getContentStylesFromPostData.js new file mode 100644 index 0000000..8d4a590 --- /dev/null +++ b/src/utils/getContentStylesFromPostData.js @@ -0,0 +1,34 @@ +import { getColorFromCloudinaryImageUrl } from './getColorFromCloudinaryImageUrl'; +import { getIsColorDark } from './getIsColorDark'; + +export const getContentStylesFromPostData = (backgroundImageURL) => { + const contentNormalColorStyle = {}; + + const contentDarkColorStyle = { + color: `var(--color-white)`, + }; + + const contentImageStyle = { + color: `var(--color-white)`, + background: `linear-gradient(180deg, rgba(0, 0, 0, 0.54) 0%, rgba(0, 0, 0, 0.54) 100%)`, + }; + + if (!backgroundImageURL) { + return contentNormalColorStyle; + } else { + const urlObj = new URL(backgroundImageURL); + const parts = urlObj.pathname.split('/'); // ['','dxho7f5dm','image','upload','v1749409044','colors','6a6a6a.png'] + const folderCandidate = parts[5]; // public_id 시작 부분 (index 5) + if (folderCandidate === 'colors') { + const imageHexColor = getColorFromCloudinaryImageUrl(backgroundImageURL); + const isDark = getIsColorDark(imageHexColor); + if (isDark) { + return contentDarkColorStyle; + } + } else if (folderCandidate === 'images') { + return contentImageStyle; + } else { + return contentImageStyle; + } + } +}; diff --git a/src/utils/getIsColorDark.js b/src/utils/getIsColorDark.js index f6d71df..2d50cd8 100644 --- a/src/utils/getIsColorDark.js +++ b/src/utils/getIsColorDark.js @@ -1,4 +1,4 @@ -export const isColorDark = (hexColor) => { +export const getIsColorDark = (hexColor) => { const { r, g, b } = hexToRgb(hexColor); const luminance = 0.299 * r + 0.587 * g + 0.114 * b; return luminance <= 128; diff --git a/src/utils/getIsImageOrColor.js b/src/utils/getIsImageOrColor.js deleted file mode 100644 index f70ad73..0000000 --- a/src/utils/getIsImageOrColor.js +++ /dev/null @@ -1,16 +0,0 @@ -//이미지 Url을 읽어와 이미지 type인지, 컬러 type인지 검사합니다. -//cloudinary 경로이고 colors 폴더에 저장된 사진이라면 'colors' -//cloudinary 경로이고 images 폴더에 저장된 사진이라면 'images' -//그 외 경로이면 'images', -//null 이면 null을 반환합니다 -export const getIsImageOrColor = (url) => { - if (!url) return null; - const urlObj = new URL(url); - const parts = urlObj.pathname.split('/'); // ['','dxho7f5dm','image','upload','v1749409044','colors','6a6a6a.png'] - const folderCandidate = parts[5]; // public_id 시작 부분 (index 5) - if (folderCandidate === 'colors' || folderCandidate === 'images') { - return folderCandidate; - } else { - return 'images'; // 기본값 - } -};