iT邦幫忙

2023 iThome 鐵人賽

DAY 5
0
SideProject30

30天製作與眾不同的TodoList吧!系列 第 5

來對我們的畫面進行一些修正吧-1.1

  • 分享至 

  • xImage
  •  

前情提要:

我們昨天把頁籤的選中時候會變色的部分製作完成,但是還少了在選中的時候進行畫面切換。
首先我們先把分頁作為一個 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 進行資料複製。

  • 這邊需要注意的是使用 map 進行複製的方式算是淺複製,現在只要知道就好。
  • 以上為 immutable 的概念。
 const newTodoList = todoList?.map((e)=>e)

這樣我們就可以得到一個新的 todoList,並且我們對他進行任何操作都不會影響到父層原本傳進來的 array 了。


上一篇
來對我們的畫面進行一些修正吧-1
下一篇
來對我們的畫面進行一些修正吧-1.2
系列文
30天製作與眾不同的TodoList吧!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言