134 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			134 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| 'use client'
 | |
| 
 | |
| import { Fragment, useEffect, useState } from 'react'
 | |
| import { useTheme } from 'next-themes'
 | |
| import { Menu, RadioGroup, Transition } from '@headlessui/react'
 | |
| 
 | |
| const Sun = () => (
 | |
|   <svg
 | |
|     xmlns="http://www.w3.org/2000/svg"
 | |
|     viewBox="0 0 20 20"
 | |
|     fill="currentColor"
 | |
|     className="group:hover:text-gray-100 h-6 w-6"
 | |
|   >
 | |
|     <path
 | |
|       fillRule="evenodd"
 | |
|       d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
 | |
|       clipRule="evenodd"
 | |
|     />
 | |
|   </svg>
 | |
| )
 | |
| const Moon = () => (
 | |
|   <svg
 | |
|     xmlns="http://www.w3.org/2000/svg"
 | |
|     viewBox="0 0 20 20"
 | |
|     fill="currentColor"
 | |
|     className="group:hover:text-gray-100 h-6 w-6"
 | |
|   >
 | |
|     <path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
 | |
|   </svg>
 | |
| )
 | |
| const Monitor = () => (
 | |
|   <svg
 | |
|     xmlns="http://www.w3.org/2000/svg"
 | |
|     viewBox="0 0 20 20"
 | |
|     fill="none"
 | |
|     stroke="currentColor"
 | |
|     strokeWidth="2"
 | |
|     strokeLinecap="round"
 | |
|     strokeLinejoin="round"
 | |
|     className="group:hover:text-gray-100 h-6 w-6"
 | |
|   >
 | |
|     <rect x="3" y="3" width="14" height="10" rx="2" ry="2"></rect>
 | |
|     <line x1="7" y1="17" x2="13" y2="17"></line>
 | |
|     <line x1="10" y1="13" x2="10" y2="17"></line>
 | |
|   </svg>
 | |
| )
 | |
| const Blank = () => <svg className="h-6 w-6" />
 | |
| 
 | |
| const ThemeSwitch = () => {
 | |
|   const [mounted, setMounted] = useState(false)
 | |
|   const { theme, setTheme, resolvedTheme } = useTheme()
 | |
| 
 | |
|   // When mounted on client, now we can show the UI
 | |
|   useEffect(() => setMounted(true), [])
 | |
| 
 | |
|   return (
 | |
|     <div className="mr-5 flex items-center">
 | |
|       <Menu as="div" className="relative inline-block text-left">
 | |
|         <div className="flex items-center justify-center hover:text-primary-500 dark:hover:text-primary-400">
 | |
|           <Menu.Button aria-label="Theme switcher">
 | |
|             {mounted ? resolvedTheme === 'dark' ? <Moon /> : <Sun /> : <Blank />}
 | |
|           </Menu.Button>
 | |
|         </div>
 | |
|         <Transition
 | |
|           as={Fragment}
 | |
|           enter="transition ease-out duration-100"
 | |
|           enterFrom="transform opacity-0 scale-95"
 | |
|           enterTo="transform opacity-100 scale-100"
 | |
|           leave="transition ease-in duration-75"
 | |
|           leaveFrom="transform opacity-100 scale-100"
 | |
|           leaveTo="transform opacity-0 scale-95"
 | |
|         >
 | |
|           <Menu.Items className="absolute right-0 z-50 mt-2 w-32 origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:bg-gray-800">
 | |
|             <RadioGroup value={theme} onChange={setTheme}>
 | |
|               <div className="p-1">
 | |
|                 <RadioGroup.Option value="light">
 | |
|                   <Menu.Item>
 | |
|                     {({ active }) => (
 | |
|                       <button
 | |
|                         className={`${
 | |
|                           active ? 'bg-primary-600 text-white' : ''
 | |
|                         } group flex w-full items-center rounded-md px-2 py-2 text-sm`}
 | |
|                       >
 | |
|                         <div className="mr-2">
 | |
|                           <Sun />
 | |
|                         </div>
 | |
|                         Light
 | |
|                       </button>
 | |
|                     )}
 | |
|                   </Menu.Item>
 | |
|                 </RadioGroup.Option>
 | |
|                 <RadioGroup.Option value="dark">
 | |
|                   <Menu.Item>
 | |
|                     {({ active }) => (
 | |
|                       <button
 | |
|                         className={`${
 | |
|                           active ? 'bg-primary-600 text-white' : ''
 | |
|                         } group flex w-full items-center rounded-md px-2 py-2 text-sm`}
 | |
|                       >
 | |
|                         <div className="mr-2">
 | |
|                           <Moon />
 | |
|                         </div>
 | |
|                         Dark
 | |
|                       </button>
 | |
|                     )}
 | |
|                   </Menu.Item>
 | |
|                 </RadioGroup.Option>
 | |
|                 <RadioGroup.Option value="system">
 | |
|                   <Menu.Item>
 | |
|                     {({ active }) => (
 | |
|                       <button
 | |
|                         className={`${
 | |
|                           active ? 'bg-primary-600 text-white' : ''
 | |
|                         } group flex w-full items-center rounded-md px-2 py-2 text-sm`}
 | |
|                       >
 | |
|                         <div className="mr-2">
 | |
|                           <Monitor />
 | |
|                         </div>
 | |
|                         System
 | |
|                       </button>
 | |
|                     )}
 | |
|                   </Menu.Item>
 | |
|                 </RadioGroup.Option>
 | |
|               </div>
 | |
|             </RadioGroup>
 | |
|           </Menu.Items>
 | |
|         </Transition>
 | |
|       </Menu>
 | |
|     </div>
 | |
|   )
 | |
| }
 | |
| 
 | |
| export default ThemeSwitch
 |