nx/nx-dev/ui-theme/src/lib/theme-switcher.component.tsx
Nicholas Cunningham 4d6cd36f5c
feat(nx-dev): new main navigation menu (#22829)
It adds a new main navigation menu on the website and in the documentation, offering more choices and simpler access to different parts of the content for the visitor.

Co-authored-by: Benjamin Cabanes <3447705+bcabanes@users.noreply.github.com>
2024-04-18 11:24:34 -04:00

97 lines
3.2 KiB
TypeScript

import { Listbox, Transition } from '@headlessui/react';
import {
ComputerDesktopIcon,
MoonIcon,
SunIcon,
} from '@heroicons/react/24/outline';
import cx from 'classnames';
import { ElementType, Fragment } from 'react';
import { Theme, useTheme } from './theme.provider';
export function ThemeSwitcher(): JSX.Element {
const [theme, setTheme] = useTheme();
const themeMap = {
dark: {
className: 'group-hover:text-white',
icon: <MoonIcon className="h-4 w-4" />,
},
light: {
className: 'group-hover:text-yellow-500',
icon: <SunIcon className="h-4 w-4" />,
},
system: {
className: 'group-hover:text-blue-500',
icon: <ComputerDesktopIcon className="h-4 w-4" />,
},
};
const availableThemes: { label: string; value: Theme; icon: ElementType }[] =
[
{
label: 'Light',
value: 'light',
icon: SunIcon,
},
{
label: 'Dark',
value: 'dark',
icon: MoonIcon,
},
{
label: 'System',
value: 'system',
icon: ComputerDesktopIcon,
},
];
return (
<div className="inline-block">
<div className="group relative flex h-full w-full items-center px-1">
<Listbox value={theme} onChange={setTheme}>
<Listbox.Label className="sr-only">Theme</Listbox.Label>
<Listbox.Button
type="button"
className={cx(
'inline-flex p-2 text-sm font-medium opacity-60 transition-opacity group-hover:opacity-100',
themeMap[theme].className
)}
>
{themeMap[theme].icon}
</Listbox.Button>
<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"
>
<Listbox.Options className="absolute top-full -right-10 z-50 mt-2 w-36 origin-top-right divide-y divide-slate-100 rounded-md bg-white shadow-lg ring-1 ring-black/5 focus:outline-none dark:divide-slate-800 dark:bg-slate-900 dark:ring-white/5">
{availableThemes.map((t) => (
<Listbox.Option key={t.value} value={t.value} as={Fragment}>
{({ active, selected }) => (
<li
className={cx(
'flex cursor-pointer items-center gap-2 px-4 py-2 text-sm',
{
'bg-slate-100 dark:bg-slate-800/60': active,
'text-blue-500 dark:text-sky-500': active || selected,
'text-slate-700 dark:text-slate-400': !active,
}
)}
>
<t.icon aria-hidden="true" className="h-4 w-4 shrink-0" />
{t.label}
</li>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</Listbox>
</div>
</div>
);
}