上一篇文章已經把原本新增 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 中。
可以用 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 的功能。在 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>
  )
}
這樣就可以正常運作了👍,操作的畫面大概會像這樣:
