iT邦幫忙

2023 iThome 鐵人賽

DAY 23
0
SideProject30

30 天用 Rust 打造 QR Code 製造機系列 第 23

Day 23 - 運用 Next.js 的 CSS Module 功能修改樣式

  • 分享至 

  • xImage
  •  

上一篇文章已經把原本新增 QR code 的 SVG 表單,重構成一個元件來使用,接下來會新增另一個元件,來製造 QR code 的 PNG 圖片。

不過在此之前要先知道,兩個表單沒有要共存在同一個頁面上,也就是說,應該會有一個切換的功能,讓使用者可以選擇要產生 SVG 或是 PNG 的 QR code。

新增 CreatePngForm 元件

首先新增一個 CreatePngForm 元件,這個元件的功能跟 CreateSvgForm 元件差不多,只是要產生的是 PNG 圖片而已,而且一些細節調整的部分也不一樣。內容的部分還不重要,要寫 Hello World 也可以,最後再 export 出來,讓 page.tsx 可以用就好。

新增一個切換的按鈕元件

接下來要新增一個切換的按鈕元件,這個元件的功能就是讓使用者可以切換要產生 SVG 或是 PNG 的 QR code。

由於是切換的功能,所以我想做一個像是開關的按鈕,然後會顯示目前是什麼格式,所以我們在 components 資料夾中新增一個 Switcher.tsx 元件,大概會是這樣:

const Switcher = () => {
  return (
    <>
      <div className='relative inline-block w-10 align-middle select-none transition duration-200 ease-in'>
        <input
          id='formSwitcher'
          type='checkbox'
          className='absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer'
          checked={}
          onChange={() => {}}
        />
        <label
          htmlFor='formSwitcher'
          className='w-12 block overflow-hidden h-6 rounded-full bg-gray-300 cursor-pointer'
        ></label>
      </div>
      <span className='ml-5 text-xl align-middle'>PNG</span>
    </>
  )
}

export default Switcher

可以看到,這個元件有一個 checked 的屬性,這個屬性是用來判斷目前是要產生 SVG 還是 PNG 的 QR code,但目前還沒有這個狀態,所以我們要從 Zustand 中新增這個狀態,然後把它傳到 checked 中。

在 Zustand Store 中新增狀態

可以用 true or false,或者是用字串也可以,這裡我們用字串,因為之後可能會有更多的格式,所以用字串比較好擴充。

import { create } from 'zustand'

interface Store {
  // 省略其他狀態
  selectedFormat: string
  setSelectedFormat: (format: string) => void
}
const useStore = create<Store>((set) => ({
  // 省略其他狀態
  selectedFormat: 'PNG',
  setSelectedFormat: (format) => set({ selectedFormat: format })
}))

Switcher 元件中讓狀態切換

然後就可以在 Switcher 元件中取得 selectedFormat 這個狀態,然後把它傳到 checked 中,這樣就可以讓使用者知道目前是要產生 SVG 還是 PNG 的 QR code。還有讓 onChange 事件可以切換狀態:

import useStore from '../store'

const Switcher = () => {
  const { selectedFormat, setSelectedFormat } = useStore()

  return (
    <>
      <div className='relative inline-block w-10 align-middle select-none transition duration-200 ease-in'>
        <input
          id='formSwitcher'
          type='checkbox'
          className='absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer'
          checked={selectedFormat === 'SVG'}
          onChange={() =>
            setSelectedFormat(selectedFormat === 'SVG' ? 'PNG' : 'SVG')
          }
        />
        <label
          htmlFor='formSwitcher'
          className='w-12 block overflow-hidden h-6 rounded-full bg-gray-300 cursor-pointer'
        ></label>
      </div>
      <span className='ml-5 text-xl align-middle'>{selectedFormat}</span>
    </>
  )
}

新增 CSS Module

因為現在點擊切換的按鈕,要讓它動態的去調整樣式,所以我們要用到 CSS Module 的功能。在 app 資料夾底下新增 styles 資料夾,並且在其中新增一個 Switcher.module.css 檔案,然後在裡面新增以下的樣式:

.toggleCheckbox:checked {
  @apply left-6 border-blue-600 bg-blue-600;
}

.toggleCheckbox {
  @apply right-6 top-0 transition-all duration-200 ease-in;
}

這裡是運用了 Tailwind CSS 的 @apply 功能,讓我們可以在 CSS 中使用 Tailwind CSS 的樣式。

接著在 Switcher.tsx 中引入這個 CSS Module:

import styles from '../styles/Switcher.module.css'

const Switcher = () => {
  // 省略其他程式碼
  <input
    id='formSwitcher'
    type='checkbox'
    className={`${styles.toggleCheckbox} absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer`}
    checked={selectedFormat === 'SVG'}
    onChange={() => setSelectedFormat(selectedFormat === 'SVG' ? 'PNG' : 'SVG')}
  />
}

最後在 page.tsx 中引入這篇文章新增的元件:

import useStore from './store'
import CreateSvgForm from './components/CreateSvgForm'
import CreatePngForm from './components/CreatePngForm'
import Switcher from './components/Switcher'

export default function Home() {
  const { imgSrc, errorsFromForm, selectedFormat } = useStore()
  // 省略其他程式碼

  return (
    <main className='flex min-h-screen flex-col items-center justify-between p-24'>
      <div className='container mx-auto p-4'>
        <h1 className='text-3xl mb-4'>QR Code 製造器</h1>
        <Switcher />
        <div className='mt-3'>
          {selectedFormat === 'SVG' ? <CreateSvgForm /> : <CreatePngForm />}
        </div>
        {imgSrc && Object.keys(errorsFromForm).length < 1 ? (
          <div className='flex justify-center mt-10'>
            <Image src={imgSrc} width={500} height={500} alt='QR Code Image' />
          </div>
        ) : (
          <p>{hasErrors ? '請輸入正確的資訊' : '請點擊「產生 QR Code」按鈕'}</p>
        )}
      </div>
    </main>
  )
}

這樣就可以正常運作了👍,操作的畫面大概會像這樣:


上一篇
Day 22 - 將表單重構成元件(下)
下一篇
Day 24 - 簡易 QR code 表單元件
系列文
30 天用 Rust 打造 QR Code 製造機30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言