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