Skip to content

Commit

Permalink
[style/datalist-component] ๐Ÿ’„Style: ๋„์„œ/CD ๋ฆฌ์ŠคํŠธ ์ปดํฌ๋„ŒํŠธ ์Šคํƒ€์ผ๋ง #16
Browse files Browse the repository at this point in the history
[style/datalist-component] ๐Ÿ’„Style: ๋„์„œ/CD ๋ฆฌ์ŠคํŠธ ์ปดํฌ๋„ŒํŠธ ์Šคํƒ€์ผ๋ง #16
  • Loading branch information
miseullang authored Feb 16, 2025
2 parents f0368bf + c54d29b commit 1017260
Show file tree
Hide file tree
Showing 16 changed files with 391 additions and 18 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@tanstack/react-query": "^5.66.0",
"@types/three": "^0.173.0",
"axios": "^1.7.9",
"classnames": "^2.5.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-router-dom": "^7.1.5",
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/assets/datalist/book-go-btn.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/assets/datalist/cd-play-btn.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/datalist/check-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/assets/datalist/check-purple.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
148 changes: 148 additions & 0 deletions src/components/datalist/DataList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import search_icon from '@assets/search-icon.svg';

import { useEffect, useRef, useState } from 'react';
import NoEditStatusItem from './NoEditStatusItem';
import EditStatusItem from './EditStatusItem';
import classNames from 'classnames';

export default function DataList({
datas,
type,
}: {
datas: DataListInfo[];
type: string;
}) {
const isBook = type === 'book' ? true : false;
const mainColor = isBook ? '#2656CD' : '#7838AF';
const debounceTimeout = useRef<NodeJS.Timeout | null>(null);

const [isEdit, setIsEdit] = useState(false);
const [currentInput, setCurrentInput] = useState('');
const [filteredDatas, setFilteredDatas] = useState<DataListInfo[]>([]);
// ์ž…๋ ฅ์ฐฝ์— focus ์—ฌ๋ถ€
const [isFocused, setIsFocused] = useState(false);

const handleDelete = () => {
console.log('deleted!');
};

const handleEdit = () => {
setIsEdit(true);
setCurrentInput('');
};

const handleComplete = () => {
setIsEdit(false);
};

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setCurrentInput(event.target.value);
};

useEffect(() => {
const filteredDatas = datas.filter((data) => {
return (
data.author?.includes(currentInput) ||
data.publisher?.includes(currentInput) ||
data.released_year?.includes(currentInput) ||
data.singer?.includes(currentInput) ||
data.title?.includes(currentInput)
);
});
// ์ž…๋ ฅ ๊ฒฐ๊ณผ ๋””๋ฐ”์šด์‹ฑ ์ ์šฉ
if (debounceTimeout.current) clearTimeout(debounceTimeout.current);
debounceTimeout.current = setTimeout(() => {
setFilteredDatas(filteredDatas);
}, 500);
}, [currentInput]);

return (
<div className='absolute top-0 right-0 w-[444px] min-h-screen bg-[#FFFAFA] rounded-tl-3xl rounded-bl-3xl '>
<div className='pl-11 pt-15 pr-10 '>
<span
className={classNames(
`text-center text-4xl font-bold leading-normal`,
isBook ? `text-[#2656CD]` : `text-[#7838AF]`,
)}>
PlayList
</span>

{/* ์ด ๊ฐฏ์ˆ˜, ํŽธ์ง‘ ๋ฒ„ํŠผ */}
<div
className={`flex items-center justify-between gap-4 mt-15 font-semibold text-[${mainColor}] `}>
<span className='text-[18px] '>{`์ด ${datas.length}๊ฐœ`}</span>

{isEdit ? (
<div className='flex items-center gap-4.5'>
<button
className={`text-[${mainColor}] cursor-pointer`}
onClick={handleDelete}>
์‚ญ์ œ
</button>
<button
className={` cursor-pointer ${
isBook ? 'text-[#3E507D80]' : 'text-[#60308C]/80 '
}`}
onClick={handleComplete}>
์™„๋ฃŒ
</button>
</div>
) : (
<button
className='cursor-pointer text-[16px]'
onClick={handleEdit}>
ํŽธ์ง‘
</button>
)}
</div>

{/* ๊ฒ€์ƒ‰์ฐฝ */}
<div
style={isFocused ? { borderColor: mainColor } : {}}
className={`relative mt-7 mb-9 border-2 border-transparent rounded-[10px] `}>
<div
className={`flex items-center w-full pl-5 pr-4 py-2.5 rounded-[10px] ${
isBook ? 'bg-[#C3D7FF26]' : 'bg-[#ddc3ff]/15'
}`}>
<input
className={` w-full focus:outline-none ${
isBook
? 'placeholder:text-[#3E507D4D]'
: 'placeholder:text-[#60308C4D]'
} `}
type='text'
placeholder='์–ด๋–ค ๊ฒƒ์ด๋“  ๊ฒ€์ƒ‰ํ•ด๋ณด์„ธ์š”!'
value={currentInput}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
onChange={handleChange}
/>
<button>
<img
src={search_icon}
alt='๊ฒ€์ƒ‰์ฐฝ ๋กœ๊ณ '
/>
</button>
</div>
</div>
<ul className='flex flex-col justify-center gap-6 pb-17 '>
{filteredDatas.map((data, index) => {
return isEdit ? (
<EditStatusItem
key={index}
data={data}
isBook={isBook}
/>
) : (
<NoEditStatusItem
key={index}
data={data}
isBook={isBook}
/>
);
})}
</ul>
</div>
</div>
);
}
63 changes: 63 additions & 0 deletions src/components/datalist/EditStatusItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import classNames from 'classnames';
import { useState } from 'react';
import check_logo from '@assets/datalist/check-logo.svg';

export default function EditStatusItem({
data,
isBook,
}: {
data: DataListInfo;
isBook: boolean;
}) {
const mainColor = isBook ? '#2656CD' : '#7838AF';
const [isChecked, setIsChecked] = useState(false);
return (
<>
<li
style={isChecked ? { borderColor: `${mainColor}` } : {}}
className={classNames(
`pl-7 pr-9 py-4.5 flex items-center gap-7 rounded-xl border-2 border-transparent ${
isBook ? 'bg-[#F1F3FA80]' : 'bg-[#f7f1fa]/50 '
}`,
)}>
<label className='relative flex items-center justify-center w-4 h-4'>
{/* ์ฒดํฌ ๋ฐ•์Šค */}
<input
type='checkbox'
style={isChecked ? { backgroundColor: mainColor } : {}}
checked={isChecked}
onChange={() => setIsChecked(!isChecked)}
className={classNames(
'appearance-none w-4 h-4 rounded-full bg-no-repeat bg-center border',
)}
/>
{isChecked && (
<img
className='absolute w-2.5 h-2.5 '
src={check_logo}
/>
)}
</label>

<div className='flex flex-col gap-2'>
<div
className={`flex items-center gap-2 ${
isBook ? 'text-[#3E507D]' : 'text-[#60308C]'
} `}>
<span className='text-[18px]'>{data.title}</span>
<span className='text-[14px]'>{data.singer || data.author}</span>
</div>

<div>
<span
className={` ${
isBook ? 'text-[#3E507DB2]' : 'text-[#5F3E7DB2]/70'
} `}>
{data.released_year}
</span>
</div>
</div>
</li>
</>
);
}
47 changes: 47 additions & 0 deletions src/components/datalist/NoEditStatusItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import cd_play_btn from '@/assets/datalist/cd-play-btn.svg';
import book_go_btn from '@/assets/datalist/book-go-btn.svg';
import { Link } from 'react-router-dom';

export default function NoEditStatusItem({
data,
isBook,
}: {
data: DataListInfo;
isBook: boolean;
}) {
return (
<>
<li
className={`pl-7 pr-9 py-4.5 flex justify-between items-center rounded-xl ${
isBook ? 'bg-[#F1F3FA80]' : 'bg-[#f7f1fa]/50'
} `}>
<div className='flex flex-col gap-2'>
<div
className={`flex items-center gap-2 ${
isBook ? 'text-[#3E507D]' : 'text-[#60308C]'
} `}>
<span className='text-[18px]'>{data.title}</span>
<span className='text-[14px]'>{data.singer || data.author}</span>
</div>

<div>
<span
className={` ${
isBook ? 'text-[#3E507DB2]' : 'text-[#5F3E7DB2]/70'
} `}>
{data.released_year}
</span>
</div>
</div>

<Link to={`${isBook ? '/book' : '/cd'}`}>
<img
className='cursor-pointer'
src={isBook ? book_go_btn : cd_play_btn}
alt={`์ด๋™ํ•˜๊ธฐ ๋ฒ„ํŠผ`}
/>
</Link>
</li>
</>
);
}
2 changes: 1 addition & 1 deletion src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ import './styles/index.css';

createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
<App />,
</StrictMode>,
);
5 changes: 5 additions & 0 deletions src/pages/cd/CdPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';

export default function CdPage() {
return <div>CdPage</div>;
}
56 changes: 56 additions & 0 deletions src/pages/cdcase/CdCasePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import DataList from '@components/datalist/DataList';
import React, { useState } from 'react';

export default function CdCasePage() {
const [isSidebarOpen, setIsSidebarOpen] = useState(false);

const dummyData = [
{
title: '์‚๋”ฑํ•˜๊ฒŒ',
singer: '๊ถŒ์ง€์šฉ',
released_year: '2019',
},
{
title: '์‚๋”ฑํ•˜๊ฒŒ',
singer: '๊ถŒ์ง€์šฉ',
released_year: '2019',
},
{
title: '์‚๋”ฑํ•˜๊ฒŒ',
singer: '๊ถŒ์ง€์šฉ',
released_year: '2019',
},
{
title: '์‚๋”ฑํ•˜๊ฒŒ',
singer: '๊ถŒ์ง€์šฉ',
released_year: '2019',
},
{
title: '์‚๋”ฑํ•˜๊ฒŒ',
singer: '๊ถŒ์ง€์šฉ',
released_year: '2019',
},
{
title: '์‚๋”ฑํ•˜๊ฒŒ',
singer: '๊ถŒ์ง€์šฉ',
released_year: '2019',
},
{
title: '์‚๋”ฑํ•˜๊ฒŒ',
singer: '๊ถŒ์ง€์šฉ',
released_year: '2019',
},
];
return (
<>
<div>CdCasePage</div>
<button onClick={() => setIsSidebarOpen(true)}>์‚ฌ์ด๋“œ๋ฐ” ์—ด๊ธฐ</button>
{isSidebarOpen && (
<DataList
type='book'
datas={dummyData}
/>
)}
</>
);
}
Loading

0 comments on commit 1017260

Please sign in to comment.