我們昨天把頁籤的選中時候會變色的部分製作完成,但是還少了在選中的時候進行畫面切換。
首先我們先把分頁作為一個 component 抽出來
const ListPage = () => {
return (
<div className="min-w-full bg-white flex flex-col items-center py-4 px-8 translate-x-[-100%]">
<h3 className="text-3xl text-gray-600 font-bold">未完成事項</h3>
<ul className="self-start w-full">
<li className="my-2 py-2 border-b-2 flex justify-between">
<span className="text-gray-600">做點什麼1</span>
<button className="bg-red-700 rounded-md w-8 h-8 flex justify-center items-center text-white">
<Cross2Icon className="w-6 h-6" />
</button>
</li>
<li className="my-2 py-2 border-b-2 flex justify-between">
<span className="text-gray-600">做點什麼2</span>
<button className="bg-red-700 rounded-md w-8 h-8 flex justify-center items-center text-white">
<Cross2Icon className="w-6 h-6" />
</button>
</li>
<li className="my-2 py-2 border-b-2 flex justify-between">
<span className="text-gray-600">做點什麼3</span>
<button className="bg-red-700 rounded-md w-8 h-8 flex justify-center items-center text-white">
<Cross2Icon className="w-6 h-6" />
</button>
</li>
<li className="my-2 py-2 border-b-2 flex justify-between">
<span className="text-gray-600">做點什麼4</span>
<button className="bg-red-700 rounded-md w-8 h-8 flex justify-center items-center text-white">
<Cross2Icon className="w-6 h-6" />
</button>
</li>
</ul>
</div>
);
};
可以看到 title 還有代辦事項的部分都是重複的 code,所以我們可以把他們抽出來。
這邊我們使用 JS Doc,來為我們的參數做點備註,這可以讓未來的自己 & 接手 code 的人更快的了解這個 component 的參數應該傳入什麼資料。
/**
* @param {string} title
* @param {Array.<string>} todoList
* */
const ListPage = ({ title, todoList }) => {
return (
<div className="min-w-full bg-white flex flex-col items-center py-4 px-8 translate-x-[-100%]">
<h3 className="text-3xl text-gray-600 font-bold">{title}</h3>
<ul className="self-start w-full">
{/* 這邊以todo的長度 > 0,判斷是否有todo */}
{todoList.length > 0 &&
todoList.map((todo, index) => (
<li
className="my-2 py-2 border-b-2 flex justify-between"
key={"todo" + index}
>
<span className="text-gray-600">{todo}</span>
<button className="bg-red-700 rounded-md w-8 h-8 flex justify-center items-center text-white">
<Cross2Icon className="w-6 h-6" />
</button>
</li>
))}
{todoList.length <= 0 && <div>目前沒有代辦事項</div>}
</ul>
</div>
);
};
然後我們可以知道之前我們在製作是否完成的畫面時,我們為什麼要讓他們並排並且超出的部分隱藏了,因為我們只要在 class 中加上 translate-x-[-100%]
就可以讓我們的頁面往左移動。
既然我們已經把這個區塊切成 component 了,那是不是應該使用一個參數讓我們可以在 component 外面進行操作?
所以這邊我們把開關isMove
作為參數丟出 component 外面。
const ListPage = ({ title, todoList, isMove }) => {
return (
// translate-x-[-100%]
<div
className={cx(
"min-w-full bg-white flex flex-col items-center py-4 px-8",
{ "translate-x-[-100%]": isMove }
)}
>
<h3 className="text-3xl text-gray-600 font-bold">{title}</h3>
<ul className="self-start w-full">
{/* 這邊以todo的長度 > 0,判斷是否有todo */}
{todoList.length > 0 &&
todoList.map((todo, index) => (
<li
className="my-2 py-2 border-b-2 flex justify-between"
key={"todo" + index}
>
<span className="text-gray-600">{todo}</span>
<button className="bg-red-700 rounded-md w-8 h-8 flex justify-center items-center text-white">
<Cross2Icon className="w-6 h-6" />
</button>
</li>
))}
{todoList.length <= 0 && <div>目前沒有代辦事項</div>}
</ul>
</div>
);
};
現在我們可以看到我們的 return 內變得乾淨許多。
<main>
<Layout>
<div className="w-1/2 min-h-[300px] bg-[#E3D5C9] rounded-md flex flex-col shadow-md py-10 gap-16">
<TodoInput />
<div className="flex justify-start w-[90%] mx-auto relative">
<div className="absolute rounded-md top-[-40px] h-[40px] left-4">
<span
className={cx(
"inline-block py-2 px-4 mr-4 rounded-t-md select-none cursor-pointer text-[1.2rem] font-bold hover:bg-gray-400 hover:text-white",
{ "bg-gray-400 text-white": isFocus.tag1 },
{ "text-gray-600 bg-white": !isFocus.tag1 }
)}
onClick={() => togglePage("tag1")}
>
未完成事項
</span>
<span
className={cx(
"inline-block py-2 px-4 mr-4 rounded-t-md select-none cursor-pointer text-[1.2rem] font-bold hover:bg-gray-400 hover:text-white",
{ "bg-gray-400 text-white": isFocus.tag2 },
{ "text-gray-600 bg-white": !isFocus.tag2 }
)}
onClick={() => togglePage("tag2")}
>
已完成事項
</span>
</div>
<div className="overflow-x-hidden w-full flex relative z-5">
<ListPage
title="未完成事項"
todoList={todoList}
isMove={isFocus.tag2}
/>
<ListPage
title="已完成事項"
todoList={todoList}
isMove={isFocus.tag2}
/>
</div>
</div>
</div>
</Layout>
</main>
但是這樣還是有點死板,所以我們可以在 component 中,加上這段transition-all duration-700
,讓我們的移動看起來更加滑順,而不是一閃一閃的。
那到目前為止我們的頁籤是不是沒有問題了?
不對,我們回頭看一下剛剛的 ListPage 這個 component。
const ListPage = ({ title, todoList, isMove }) => {
return (
// translate-x-[-100%]
<div
className={cx(
"min-w-full bg-white flex flex-col items-center py-4 px-8 transition-all duration-700",
{ "translate-x-[-100%]": isMove }
)}
>
<h3 className="text-3xl text-gray-600 font-bold">{title}</h3>
<ul className="self-start w-full">
{/* 這邊以todo的長度 > 0,判斷是否有todo */}
{todoList.length > 0 &&
todoList.map((todo, index) => (
<li
className="my-2 py-2 border-b-2 flex justify-between"
key={"todo" + index}
>
<span className="text-gray-600">{todo}</span>
<button className="bg-red-700 rounded-md w-8 h-8 flex justify-center items-center text-white">
<Cross2Icon className="w-6 h-6" />
</button>
</li>
))}
{todoList.length <= 0 && <div>目前沒有代辦事項</div>}
</ul>
</div>
);
};
可以看到我們下面使用的資料就是父層傳進來的 todoList 這個列表,那也許有人會說。
沒問題阿,你看~資料都跑得出來。
確實到目前為止好像都沒有甚麼問題,但下來我們要為每個選項後面的刪除按鈕添加功能就會出現問題了,因為我們在點擊刪除的時候會做的事情就是進入 todoList 這個陣列找到相應的資料進行刪除,來達成我們移除 tood 的目的。
所以我們為了不改變原始的資料來源,可以使用map
這個 method 進行資料複製。
const newTodoList = todoList?.map((e)=>e)
這樣我們就可以得到一個新的 todoList,並且我們對他進行任何操作都不會影響到父層原本傳進來的 array 了。